Merge branch 'dev' into signature_verifier

This commit is contained in:
Milos Kozak 2019-07-26 22:28:22 +02:00 committed by GitHub
commit b448bbe670
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
615 changed files with 39901 additions and 2116 deletions

View file

@ -2,19 +2,19 @@ language: android
jdk: oraclejdk8
env:
matrix:
- ANDROID_TARGET=android-23 ANDROID_ABI=x86 org.gradle.jvmargs=-XX:-OmitStackTraceInFastThrow
- ANDROID_TARGET=android-28 ANDROID_ABI=x86 org.gradle.jvmargs=-XX:-OmitStackTraceInFastThrow
android:
components:
- platform-tools
- tools
- build-tools-27.0.2
- android-23
- build-tools-28.0.3
- android-28
- extra-google-m2repository
- extra-android-m2repository
- extra-google-google_play_services
before_install:
- yes | sdkmanager "platforms;android-27"
#- yes | sdkmanager "platforms;android-28"
script:
# Unit Test

View file

@ -11,4 +11,4 @@
dev: [![codecov](https://codecov.io/gh/MilosKozak/AndroidAPS/branch/dev/graph/badge.svg)](https://codecov.io/gh/MilosKozak/AndroidAPS)
[![Donate via PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y4LHGJJESAVB8)
[![Donate via PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y4LHGJJESAVB8)

View file

@ -6,23 +6,30 @@ buildscript {
dependencies {
classpath 'io.fabric.tools:gradle:1.+'
classpath 'com.dicedmelon.gradle:jacoco-android:0.1.3'
classpath 'com.dicedmelon.gradle:jacoco-android:0.1.4'
classpath 'de.undercouch:gradle-download-task:3.4.3'
}
}
apply plugin: "com.android.application"
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.gms.google-services'
apply plugin: "io.fabric"
apply plugin: "jacoco-android"
apply plugin: 'io.fabric'
apply plugin: 'jacoco-android'
apply plugin: 'com.jakewharton.butterknife'
apply plugin: 'de.undercouch.download'
jacoco {
toolVersion = "0.8.3"
}
ext {
supportLibraryVersion = "27.1.1"
supportLibraryVersion = "28.0.0"
ormLiteVersion = "4.46"
powermockVersion = "1.7.3"
dexmakerVersion = "1.2"
butterknifeVersion = "8.8.1"
butterknifeVersion = "10.1.0"
}
@ -79,7 +86,7 @@ def allCommited = { ->
try {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'status'
commandLine 'git', 'status', '-s'
standardOutput = stdout
}
String commitObject = stdout.toString().trim()
@ -87,7 +94,8 @@ def allCommited = { ->
} catch (ignored) {
return false; // NoGitSystemAvailable
}
return stringBuilder.toString().contains("nothing to commit")
return stringBuilder.toString().isEmpty()
}
tasks.matching { it instanceof Test }.all {
@ -96,19 +104,19 @@ tasks.matching { it instanceof Test }.all {
}
android {
compileSdkVersion 27
compileSdkVersion 28
defaultConfig {
minSdkVersion 21
targetSdkVersion 25
minSdkVersion 23
targetSdkVersion 28
multiDexEnabled true
versionCode 1500
version "2.3.1-dev"
version "2.4-dev"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// if you change minSdkVersion to less than 11, you need to change executeTask for wear
ndk {
@ -187,8 +195,15 @@ android {
}
testOptions {
unitTests.returnDefaultValues = true
unitTests.includeAndroidResources = true
unitTests {
returnDefaultValues = true
includeAndroidResources = true
all {
maxParallelForks = 10
forkEvery = 20
}
}
}
useLibrary "org.apache.http.legacy"
@ -200,34 +215,34 @@ allprojects {
flatDir {
dirs 'libs'
}
maven { url 'https://jitpack.io' }
}
}
configurations {
libs
}
dependencies {
wearApp project(':wear')
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.android.gms:play-services-wearable:16.0.1'
implementation 'com.google.firebase:firebase-core:16.0.8'
implementation 'com.google.android.gms:play-services-wearable:17.0.0'
implementation 'com.google.firebase:firebase-core:17.0.1'
implementation("com.crashlytics.sdk.android:crashlytics:2.9.9@aar") {
transitive = true;
}
libs "MilosKozak:danars-support-lib:master@zip"
implementation "com.android.support:appcompat-v7:${supportLibraryVersion}"
implementation "com.android.support:support-v13:${supportLibraryVersion}"
implementation "com.android.support:support-v4:${supportLibraryVersion}"
implementation "com.android.support:cardview-v7:${supportLibraryVersion}"
implementation "com.android.support:recyclerview-v7:${supportLibraryVersion}"
implementation "com.android.support:gridlayout-v7:${supportLibraryVersion}"
implementation "com.android.support:design:${supportLibraryVersion}"
implementation "com.android.support:percent:${supportLibraryVersion}"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.percentlayout:percentlayout:1.0.0'
implementation "com.wdullaer:materialdatetimepicker:2.3.0"
// Otto bus will be replaced by rx
implementation "com.squareup:otto:1.3.7"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "com.j256.ormlite:ormlite-core:${ormLiteVersion}"
implementation "com.j256.ormlite:ormlite-android:${ormLiteVersion}"
implementation("com.github.tony19:logback-android-classic:1.1.1-6") {
@ -238,7 +253,7 @@ dependencies {
// Graphview cannot be upgraded
implementation "com.jjoe64:graphview:4.0.1"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.1.1"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation(name: "android-edittext-validator-v1.3.4-mod", ext: "aar")
implementation 'com.madgag.spongycastle:core:1.58.0.0'
@ -249,7 +264,7 @@ dependencies {
// excluding org.json which is provided by Android
exclude group: "org.json", module: "json"
}
implementation "com.google.code.gson:gson:2.8.2"
implementation "com.google.code.gson:gson:2.8.5"
implementation "com.google.guava:guava:24.1-jre"
implementation "net.danlew:android.joda:2.9.9.1"
@ -260,6 +275,8 @@ dependencies {
implementation "com.jakewharton:butterknife:${butterknifeVersion}"
annotationProcessor "com.jakewharton:butterknife-compiler:${butterknifeVersion}"
implementation 'com.github.DavidProdinger:weekdays-selector:1.0.4'
testImplementation "junit:junit:4.12"
testImplementation "org.json:json:20140107"
testImplementation "org.mockito:mockito-core:2.8.47"
@ -268,26 +285,48 @@ dependencies {
testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}"
testImplementation "joda-time:joda-time:2.9.9"
testImplementation "com.google.truth:truth:0.39"
testImplementation 'org.robolectric:robolectric:3.8'
testImplementation("com.google.truth:truth:0.39") {
exclude group: "com.google.guava", module: "guava"
}
testImplementation("org.robolectric:robolectric:4.2.1") {
exclude group: "com.google.guava", module: "guava"
}
testImplementation "org.skyscreamer:jsonassert:1.5.0"
testImplementation "org.hamcrest:hamcrest-all:1.3"
testImplementation("uk.org.lidalia:slf4j-test:1.2.0") {
exclude group: "com.google.guava", module: "guava"
}
androidTestImplementation "org.mockito:mockito-core:2.8.47"
androidTestImplementation "com.google.dexmaker:dexmaker:${dexmakerVersion}"
androidTestImplementation "com.google.dexmaker:dexmaker-mockito:${dexmakerVersion}"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
// new for tidepool
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation "com.squareup.retrofit2:retrofit:2.4.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0"
implementation "com.squareup.retrofit2:converter-gson:2.4.0"
}
task unzip(type: Copy) {
def zipPath = configurations.libs.find { it.name.startsWith("danars") }
def zipFile = file(zipPath)
def outputDir = file("${buildDir}/unpacked/dist")
from zipTree(zipFile)
task downloadZipFile(type: Download) {
src 'https://github.com/MilosKozak/danars-support-lib/archive/master.zip'
dest new File(buildDir, 'danars.zip')
}
task downloadAndUnzipFile(dependsOn: downloadZipFile, type: Copy) {
from zipTree(downloadZipFile.dest)
def outputDir = file("${buildDir}/unpacked/dist")
into outputDir
}
task copyLibs(dependsOn: unzip, type: Copy) {
task copyLibs(dependsOn: downloadAndUnzipFile, type: Copy) {
def src = file("${buildDir}/unpacked/dist/danars-support-lib-master")
def target = file("src/main/jniLibs/")

View file

@ -17,10 +17,12 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="sugar.free.sightremote.HISTORY_BROADCASTS" />
<uses-permission android:name="com.dexcom.cgm.EXTERNAL_PERMISSION" />
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
@ -34,13 +36,15 @@
android:label="@string/app_name"
android:roundIcon="${appIconRound}"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/AppTheme.NoActionBar"
android:fullBackupContent="true">
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
@ -85,10 +89,8 @@
<action android:name="com.eveningoutpost.dexdrip.NS_EMULATOR"/>
<!-- Receiver from glimp -->
<action android:name="it.ct.glicemia.ACTION_GLUCOSE_MEASURED"/>
<!-- Receiver from DexcomG5 -->
<action android:name="com.dexcom.cgm.DATA"/>
<action android:name="com.dexcom.cgm.AndroidAPSEVGCallback.BROADCAST"/>
<action android:name="com.dexcom.cgm.g5.AndroidAPSEVGCallback.BROADCAST"/>
<!-- Receiver from Dexcom -->
<action android:name="com.dexcom.cgm.EXTERNAL_BROADCAST"/>
<!-- Receiver from Poctech -->
<action android:name="com.china.poctech.data"/>
<!-- Receiver from Tomato -->
@ -147,7 +149,7 @@
</receiver>
<provider
android:name="android.support.v4.content.FileProvider"
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
@ -160,6 +162,9 @@
<service
android:name=".services.DataService"
android:exported="false" />
<service
android:name=".services.LocationService"
android:exported="false" />
<service
android:name=".plugins.pump.danaR.services.DanaRExecutionService"
android:enabled="true"
@ -281,6 +286,27 @@
android:name=".plugins.pump.insight.activities.InsightPairingInformationActivity"
android:label="@string/pairing_information"
android:theme="@style/AppTheme" />
<activity android:name=".activities.RequestDexcomPermissionActivity" />
<!-- Medtronic service and activities -->
<service
android:name=".plugins.pump.medtronic.service.RileyLinkMedtronicService"
android:enabled="true"
android:exported="true" />
<activity android:name=".plugins.pump.common.dialog.RileyLinkBLEScanActivity">
<intent-filter>
<action android:name="info.nightscout.androidaps.plugins.PumpCommon.dialog.RileyLinkBLEScanActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusActivity"
android:label="@string/title_activity_rileylink_settings"
android:theme="@style/Theme.AppCompat.NoTitle" />
<activity android:name=".plugins.pump.medtronic.dialog.MedtronicHistoryActivity" />
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
</application>
</manifest>

View file

@ -31,7 +31,7 @@ public class Constants {
public static final long remoteBolusMinDistance = 15 * 60 * 1000L;
// Circadian Percentage Profile
public static final int CPP_MIN_PERCENTAGE = 50;
public static final int CPP_MIN_PERCENTAGE = 30;
public static final int CPP_MAX_PERCENTAGE = 200;
public static final int CPP_MIN_TIMESHIFT = -6;
public static final int CPP_MAX_TIMESHIFT = 23;

View file

@ -7,16 +7,16 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.support.annotation.Nullable;
import android.support.design.widget.NavigationView;
import android.support.design.widget.TabLayout;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import androidx.annotation.Nullable;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.tabs.TabLayout;
import androidx.core.app.ActivityCompat;
import androidx.viewpager.widget.ViewPager;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;

View file

@ -1,12 +1,18 @@
package info.nightscout.androidaps;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.annotation.PluralsRes;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.PluralsRes;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.crashlytics.android.Crashlytics;
import com.google.firebase.analytics.FirebaseAnalytics;
@ -39,6 +45,7 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugi
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin;
import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintPlugin;
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment;
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin;
import info.nightscout.androidaps.plugins.general.food.FoodPlugin;
import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils;
@ -51,6 +58,7 @@ import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin;
import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin;
import info.nightscout.androidaps.plugins.general.signatureVerifier.SignatureVerifier;
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin;
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin;
import info.nightscout.androidaps.plugins.general.versionChecker.VersionCheckerPlugin;
import info.nightscout.androidaps.plugins.general.wear.WearPlugin;
import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin;
@ -62,19 +70,21 @@ import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin;
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin;
import info.nightscout.androidaps.plugins.profile.simple.SimpleProfilePlugin;
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin;
import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin;
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin;
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin;
import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin;
import info.nightscout.androidaps.plugins.source.SourceDexcomG6Plugin;
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
import info.nightscout.androidaps.plugins.source.SourceEversensePlugin;
import info.nightscout.androidaps.plugins.source.SourceGlimpPlugin;
import info.nightscout.androidaps.plugins.source.SourceMM640gPlugin;
@ -86,6 +96,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.receivers.DataReceiver;
import info.nightscout.androidaps.receivers.KeepAliveReceiver;
import info.nightscout.androidaps.receivers.NSAlarmReceiver;
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver;
import info.nightscout.androidaps.services.Intents;
import info.nightscout.androidaps.utils.FabricPrivacy;
import io.fabric.sdk.android.Fabric;
@ -113,6 +124,8 @@ public class MainApp extends Application {
private static AckAlarmReceiver ackAlarmReciever = new AckAlarmReceiver();
private static DBAccessReceiver dbAccessReciever = new DBAccessReceiver();
private LocalBroadcastManager lbm;
BroadcastReceiver btReceiver;
TimeDateOrTZChangeReceiver timeDateOrTZChangeReceiver;
public static boolean devBranch;
public static boolean engineeringMode;
@ -146,7 +159,7 @@ public class MainApp extends Application {
File engineeringModeSemaphore = new File(extFilesDir, "engineering_mode");
engineeringMode = engineeringModeSemaphore.exists() && engineeringModeSemaphore.isFile();
devBranch = BuildConfig.VERSION.contains("dev");
devBranch = BuildConfig.VERSION.contains("-") || BuildConfig.VERSION.matches(".*[a-zA-Z]+.*");
sBus = L.isEnabled(L.EVENTS) && devBranch ? new LoggingBus(ThreadEnforcer.ANY) : new Bus(ThreadEnforcer.ANY);
@ -154,6 +167,7 @@ public class MainApp extends Application {
//trigger here to see the new version on app start after an update
triggerCheckVersion();
//setBTReceiver();
if (pluginsList == null) {
pluginsList = new ArrayList<>();
@ -175,6 +189,7 @@ public class MainApp extends Application {
if (Config.PUMPDRIVERS) pluginsList.add(LocalInsightPlugin.getPlugin());
pluginsList.add(CareportalPlugin.getPlugin());
if (Config.PUMPDRIVERS) pluginsList.add(ComboPlugin.getPlugin());
if (Config.PUMPDRIVERS && engineeringMode) pluginsList.add(MedtronicPumpPlugin.getPlugin());
if (Config.MDI) pluginsList.add(MDIPlugin.getPlugin());
pluginsList.add(VirtualPumpPlugin.getPlugin());
if (Config.APS) pluginsList.add(LoopPlugin.getPlugin());
@ -194,8 +209,7 @@ public class MainApp extends Application {
pluginsList.add(SourceNSClientPlugin.getPlugin());
pluginsList.add(SourceMM640gPlugin.getPlugin());
pluginsList.add(SourceGlimpPlugin.getPlugin());
pluginsList.add(SourceDexcomG5Plugin.getPlugin());
pluginsList.add(SourceDexcomG6Plugin.getPlugin());
pluginsList.add(SourceDexcomPlugin.INSTANCE);
pluginsList.add(SourcePoctechPlugin.getPlugin());
pluginsList.add(SourceTomatoPlugin.getPlugin());
pluginsList.add(SourceEversensePlugin.getPlugin());
@ -206,7 +220,11 @@ public class MainApp extends Application {
pluginsList.add(StatuslinePlugin.initPlugin(this));
pluginsList.add(PersistentNotificationPlugin.getPlugin());
pluginsList.add(NSClientPlugin.getPlugin());
if (engineeringMode)
pluginsList.add(TidepoolPlugin.INSTANCE);
pluginsList.add(MaintenancePlugin.initPlugin(this));
if (engineeringMode)
pluginsList.add(AutomationPlugin.INSTANCE);
pluginsList.add(ConfigBuilderPlugin.getPlugin());
@ -254,6 +272,10 @@ public class MainApp extends Application {
//register dbaccess
lbm.registerReceiver(dbAccessReciever, new IntentFilter(Intents.ACTION_DATABASE));
this.timeDateOrTZChangeReceiver = new TimeDateOrTZChangeReceiver();
this.timeDateOrTZChangeReceiver.registerBroadcasts(this);
}
private void startKeepAliveService() {
@ -439,5 +461,19 @@ public class MainApp extends Application {
sDatabaseHelper.close();
sDatabaseHelper = null;
}
if (btReceiver != null) {
unregisterReceiver(btReceiver);
}
if (timeDateOrTZChangeReceiver!=null) {
unregisterReceiver(timeDateOrTZChangeReceiver);
}
}
public static int dpToPx(int dp) {
float scale = sResources.getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}

View file

@ -2,9 +2,9 @@ package info.nightscout.androidaps.activities;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.PopupMenu;
import androidx.core.content.res.ResourcesCompat;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.Menu;
@ -52,7 +52,7 @@ public class HistoryBrowseActivity extends AppCompatActivity {
ImageButton chartButton;
boolean showBasal = true;
boolean showIob, showCob, showDev, showRat, showDevslope;
boolean showIob, showCob, showDev, showRat, showActPrim, showActSec, showDevslope;
@BindView(R.id.historybrowse_date)
@ -285,6 +285,10 @@ public class HistoryBrowseActivity extends AppCompatActivity {
// set manual x bounds to have nice steps
graphData.formatAxis(fromTime, toTime);
if(showActPrim) {
graphData.addActivity(fromTime, toTime, false,1d);
}
// Treatments
graphData.addTreatments(fromTime, toTime);
@ -305,6 +309,7 @@ public class HistoryBrowseActivity extends AppCompatActivity {
boolean useCobForScale = false;
boolean useDevForScale = false;
boolean useRatioForScale = false;
boolean useIAForScale = false;
boolean useDSForScale = false;
if (showIob) {
@ -315,6 +320,8 @@ public class HistoryBrowseActivity extends AppCompatActivity {
useDevForScale = true;
} else if (showRat) {
useRatioForScale = true;
} else if (showActSec) {
useIAForScale = true;
} else if (showDevslope) {
useDSForScale = true;
}
@ -327,6 +334,8 @@ public class HistoryBrowseActivity extends AppCompatActivity {
secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1d);
if (showRat)
secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1d);
if (showActSec)
secondGraphData.addActivity(fromTime, toTime, useIAForScale, useIAForScale? 2d: 1d);
if (showDevslope)
secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1d);
@ -337,14 +346,14 @@ public class HistoryBrowseActivity extends AppCompatActivity {
// do GUI update
runOnUiThread(() -> {
if (showIob || showCob || showDev || showRat || showDevslope) {
if (showIob || showCob || showDev || showRat || showActSec || showDevslope) {
iobGraph.setVisibility(View.VISIBLE);
} else {
iobGraph.setVisibility(View.GONE);
}
// finally enforce drawing of graphs
graphData.performUpdate();
if (showIob || showCob || showDev || showRat || showDevslope)
if (showIob || showCob || showDev || showRat || showActSec || showDevslope)
secondGraphData.performUpdate();
});
}).start();
@ -353,22 +362,37 @@ public class HistoryBrowseActivity extends AppCompatActivity {
private void setupChartMenu() {
chartButton = (ImageButton) findViewById(R.id.overview_chartMenuButton);
chartButton.setOnClickListener(v -> {
MenuItem item;
MenuItem item,dividerItem;
CharSequence title;
int titleMaxChars = 0;
SpannableString s;
PopupMenu popup = new PopupMenu(v.getContext(), v);
item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.BAS.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_basals));
title = item.getTitle();
if (titleMaxChars < title.length()) titleMaxChars = title.length();
s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.basal, null)), 0, s.length(), 0);
item.setTitle(s);
item.setCheckable(true);
item.setChecked(showBasal);
item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.ACTPRIM.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_activity));
title = item.getTitle();
if (titleMaxChars < title.length()) titleMaxChars = title.length();
s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.activity, null)), 0, s.length(), 0);
item.setTitle(s);
item.setCheckable(true);
item.setChecked(showActPrim);
dividerItem = popup.getMenu().add("");
dividerItem.setEnabled(false);
item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.IOB.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_iob));
title = item.getTitle();
if (titleMaxChars < title.length()) titleMaxChars = title.length();
s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.iob, null)), 0, s.length(), 0);
item.setTitle(s);
@ -377,6 +401,7 @@ public class HistoryBrowseActivity extends AppCompatActivity {
item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.COB.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_cob));
title = item.getTitle();
if (titleMaxChars < title.length()) titleMaxChars = title.length();
s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.cob, null)), 0, s.length(), 0);
item.setTitle(s);
@ -385,6 +410,7 @@ public class HistoryBrowseActivity extends AppCompatActivity {
item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.DEV.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_deviations));
title = item.getTitle();
if (titleMaxChars < title.length()) titleMaxChars = title.length();
s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.deviations, null)), 0, s.length(), 0);
item.setTitle(s);
@ -393,15 +419,27 @@ public class HistoryBrowseActivity extends AppCompatActivity {
item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.SEN.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_sensitivity));
title = item.getTitle();
if (titleMaxChars < title.length()) titleMaxChars = title.length();
s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.ratio, null)), 0, s.length(), 0);
item.setTitle(s);
item.setCheckable(true);
item.setChecked(showRat);
item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.ACTSEC.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_activity));
title = item.getTitle();
if (titleMaxChars < title.length()) titleMaxChars = title.length();
s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.activity, null)), 0, s.length(), 0);
item.setTitle(s);
item.setCheckable(true);
item.setChecked(showActSec);
if (MainApp.devBranch) {
item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.DEVSLOPE.ordinal(), Menu.NONE, "Deviation slope");
title = item.getTitle();
if (titleMaxChars < title.length()) titleMaxChars = title.length();
s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.devslopepos, null)), 0, s.length(), 0);
item.setTitle(s);
@ -409,6 +447,10 @@ public class HistoryBrowseActivity extends AppCompatActivity {
item.setChecked(showDevslope);
}
// Fairly good guestimate for required divider text size...
title = new String(new char[titleMaxChars+10]).replace("\0", "_");
dividerItem.setTitle(title);
popup.setOnMenuItemClickListener(item1 -> {
if (item1.getItemId() == OverviewFragment.CHARTTYPE.BAS.ordinal()) {
showBasal = !item1.isChecked();
@ -420,6 +462,10 @@ public class HistoryBrowseActivity extends AppCompatActivity {
showDev = !item1.isChecked();
} else if (item1.getItemId() == OverviewFragment.CHARTTYPE.SEN.ordinal()) {
showRat = !item1.isChecked();
} else if (item1.getItemId() == OverviewFragment.CHARTTYPE.ACTPRIM.ordinal()) {
showActPrim = !item1.isChecked();
} else if (item1.getItemId() == OverviewFragment.CHARTTYPE.ACTSEC.ordinal()) {
showActSec = !item1.isChecked();
} else if (item1.getItemId() == OverviewFragment.CHARTTYPE.DEVSLOPE.ordinal()) {
showDevslope = !item1.isChecked();
}

View file

@ -15,12 +15,15 @@ import android.text.TextUtils;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.events.EventRefreshGui;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin;
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin;
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin;
import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader;
import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
@ -33,18 +36,20 @@ import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin;
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin;
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin;
import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin;
import info.nightscout.androidaps.plugins.general.wear.WearPlugin;
import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin;
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
import info.nightscout.androidaps.utils.LocaleHelper;
import info.nightscout.androidaps.utils.OKDialog;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
MyPreferenceFragment myPreferenceFragment;
@ -63,6 +68,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
MainApp.bus().post(new EventPreferenceChange(key));
RxBus.INSTANCE.send(new EventPreferenceChange(key));
if (key.equals("language")) {
String lang = sharedPreferences.getString("language", "en");
LocaleHelper.setLocale(getApplicationContext(), lang);
@ -93,7 +99,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
} else if (editTextPref.getText() != null) {
((EditTextPreference) pref).setDialogMessage(editTextPref.getDialogMessage());
pref.setSummary(editTextPref.getText());
} else if (pref.getKey().contains("smscommunicator_allowednumbers") && TextUtils.isEmpty(editTextPref.getText().trim())) {
} else if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) {
pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary));
}
}
@ -144,7 +150,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
addPreferencesFromResource(R.xml.pref_overview);
addPreferencesFromResourceIfEnabled(SourceDexcomG5Plugin.getPlugin(), PluginType.BGSOURCE);
addPreferencesFromResourceIfEnabled(SourceDexcomPlugin.INSTANCE, PluginType.BGSOURCE);
addPreferencesFromResourceIfEnabled(CareportalPlugin.getPlugin(), PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(SafetyPlugin.getPlugin(), PluginType.CONSTRAINTS);
if (Config.APS) {
@ -166,6 +172,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
addPreferencesFromResourceIfEnabled(DanaRSPlugin.getPlugin(), PluginType.PUMP);
addPreferencesFromResourceIfEnabled(LocalInsightPlugin.getPlugin(), PluginType.PUMP);
addPreferencesFromResourceIfEnabled(ComboPlugin.getPlugin(), PluginType.PUMP);
addPreferencesFromResourceIfEnabled(MedtronicPumpPlugin.getPlugin(), PluginType.PUMP);
if (DanaRPlugin.getPlugin().isEnabled(PluginType.PROFILE)
|| DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PROFILE)
@ -182,7 +189,9 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
addPreferencesFromResourceIfEnabled(InsulinOrefFreePeakPlugin.getPlugin(), PluginType.INSULIN);
addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResource(R.xml.pref_others);
addPreferencesFromResource(R.xml.pref_datachoices);
@ -192,7 +201,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
}
if (Config.NSCLIENT) {
PreferenceScreen scrnAdvancedSettings = (PreferenceScreen)findPreference(getString(R.string.key_advancedsettings));
PreferenceScreen scrnAdvancedSettings = (PreferenceScreen) findPreference(getString(R.string.key_advancedsettings));
if (scrnAdvancedSettings != null) {
scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_res_warning)));
scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_res_critical)));
@ -203,6 +212,13 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
}
initSummary(getPreferenceScreen());
final Preference tidepoolTestLogin = findPreference(MainApp.gs(R.string.key_tidepool_test_login));
if (tidepoolTestLogin != null)
tidepoolTestLogin.setOnPreferenceClickListener(preference -> {
TidepoolUploader.INSTANCE.testLogin(getActivity());
return false;
});
}
@Override

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.activities
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin
class RequestDexcomPermissionActivity : AppCompatActivity() {
private val requestCode = "AndroidAPS <3".map { it.toInt() }.sum()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(arrayOf(SourceDexcomPlugin.PERMISSION), requestCode)
} else {
finish()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
finish()
}
}

View file

@ -2,9 +2,9 @@ package info.nightscout.androidaps.activities;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.appcompat.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;

View file

@ -4,7 +4,7 @@ import android.app.Activity;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.MotionEvent;

View file

@ -1,11 +1,9 @@
package info.nightscout.androidaps.data;
import android.support.annotation.NonNull;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import javax.annotation.Nonnull;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.interfaces.Constraint;

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.data;
import android.support.annotation.Nullable;
import android.support.v4.util.LongSparseArray;
import androidx.annotation.Nullable;
import androidx.collection.LongSparseArray;
import java.util.ArrayList;
import java.util.List;

View file

@ -1,10 +1,8 @@
package info.nightscout.androidaps.data;
import android.support.annotation.Nullable;
import android.support.v4.util.LongSparseArray;
import androidx.annotation.Nullable;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.interfaces.Interval;
/**
@ -21,7 +19,7 @@ public class NonOverlappingIntervals<T extends Interval> extends Intervals<T> {
rawData = other.rawData.clone();
}
protected synchronized void merge() {
public synchronized void merge() {
for (int index = 0; index < rawData.size() - 1; index++) {
Interval i = rawData.valueAt(index);
long startOfNewer = rawData.valueAt(index + 1).start();

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.data;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import info.nightscout.androidaps.interfaces.Interval;

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.data;
import android.support.v4.util.LongSparseArray;
import androidx.collection.LongSparseArray;
import org.json.JSONArray;
import org.json.JSONException;
@ -400,6 +400,19 @@ public class Profile {
return getValuesList(isf_v, null, new DecimalFormat("0.0"), getUnits() + MainApp.gs(R.string.profile_per_unit));
}
public ProfileValue[] getIsfs() {
if (isf_v == null)
isf_v = convertToSparseArray(ic);
ProfileValue[] ret = new ProfileValue[isf_v.size()];
for (Integer index = 0; index < isf_v.size(); index++) {
Integer tas = (int) isf_v.keyAt(index);
double value = isf_v.valueAt(index);
ret[index] = new ProfileValue(tas, value);
}
return ret;
}
public double getIc() {
return getIcTimeFromMidnight(secondsFromMidnight());
}
@ -420,6 +433,19 @@ public class Profile {
return getValuesList(ic_v, null, new DecimalFormat("0.0"), MainApp.gs(R.string.profile_carbs_per_unit));
}
public ProfileValue[] getIcs() {
if (ic_v == null)
ic_v = convertToSparseArray(ic);
ProfileValue[] ret = new ProfileValue[ic_v.size()];
for (Integer index = 0; index < ic_v.size(); index++) {
Integer tas = (int) ic_v.keyAt(index);
double value = ic_v.valueAt(index);
ret[index] = new ProfileValue(tas, value);
}
return ret;
}
public double getBasal() {
return getBasalTimeFromMidnight(secondsFromMidnight());
}
@ -441,8 +467,8 @@ public class Profile {
return getValuesList(basal_v, null, new DecimalFormat("0.00"), MainApp.gs(R.string.profile_ins_units_per_hout));
}
public class BasalValue {
public BasalValue(int timeAsSeconds, double value) {
public class ProfileValue {
public ProfileValue(int timeAsSeconds, double value) {
this.timeAsSeconds = timeAsSeconds;
this.value = value;
}
@ -451,15 +477,15 @@ public class Profile {
public double value;
}
public synchronized BasalValue[] getBasalValues() {
public synchronized ProfileValue[] getBasalValues() {
if (basal_v == null)
basal_v = convertToSparseArray(basal);
BasalValue[] ret = new BasalValue[basal_v.size()];
ProfileValue[] ret = new ProfileValue[basal_v.size()];
for (Integer index = 0; index < basal_v.size(); index++) {
Integer tas = (int) basal_v.keyAt(index);
double value = basal_v.valueAt(index);
ret[index] = new BasalValue(tas, value);
ret[index] = new ProfileValue(tas, value);
}
return ret;
}
@ -500,6 +526,49 @@ public class Profile {
return getValueToTime(targetHigh_v, timeAsSeconds);
}
public class TargetValue {
public TargetValue(int timeAsSeconds, double low, double high) {
this.timeAsSeconds = timeAsSeconds;
this.low = low;
this.high = high;
}
public int timeAsSeconds;
public double low;
public double high;
}
public TargetValue[] getTargets() {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
TargetValue[] ret = new TargetValue[targetLow_v.size()];
for (Integer index = 0; index < targetLow_v.size(); index++) {
Integer tas = (int) targetLow_v.keyAt(index);
double low = targetLow_v.valueAt(index);
double high = targetHigh_v.valueAt(index);
ret[index] = new TargetValue(tas, low, high);
}
return ret;
}
public ProfileValue[] getSingleTargets() {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
ProfileValue[] ret = new ProfileValue[targetLow_v.size()];
for (Integer index = 0; index < targetLow_v.size(); index++) {
Integer tas = (int) targetLow_v.keyAt(index);
double target = (targetLow_v.valueAt(index) + targetHigh_v.valueAt(index)) / 2;
ret[index] = new ProfileValue(tas, target);
}
return ret;
}
public String getTargetList() {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.data;
import android.support.annotation.Nullable;
import android.support.v4.util.LongSparseArray;
import androidx.annotation.Nullable;
import androidx.collection.LongSparseArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.data;
import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;
import androidx.annotation.Nullable;
import androidx.collection.ArrayMap;
import org.json.JSONException;
import org.json.JSONObject;

View file

@ -45,6 +45,11 @@ public class PumpEnactResult {
return this;
}
public PumpEnactResult comment(int comment) {
this.comment = MainApp.gs(comment);
return this;
}
public PumpEnactResult duration(int duration) {
this.duration = duration;
return this;

View file

@ -50,7 +50,7 @@ public class QuickWizardEntry {
useTemptarget: 0
}
*/
public QuickWizardEntry() {
QuickWizardEntry() {
String emptyData = "{\"buttonText\":\"\",\"carbs\":0,\"validFrom\":0,\"validTo\":86340}";
try {
storage = new JSONObject(emptyData);
@ -60,18 +60,17 @@ public class QuickWizardEntry {
position = -1;
}
public QuickWizardEntry(JSONObject entry, int position) {
QuickWizardEntry(JSONObject entry, int position) {
storage = entry;
this.position = position;
}
public Boolean isActive() {
Boolean isActive() {
return Profile.secondsFromMidnight() >= validFrom() && Profile.secondsFromMidnight() <= validTo();
}
public BolusWizard doCalc(Profile profile, TempTarget tempTarget, BgReading lastBG, boolean _synchronized) {
BolusWizard wizard = new BolusWizard();
public BolusWizard doCalc(Profile profile, String profileName, BgReading lastBG, boolean _synchronized) {
final TempTarget tempTarget = TreatmentsPlugin.getPlugin().getTempTargetFromHistory();
//BG
double bg = 0;
if (lastBG != null && useBG() == YES) {
@ -86,11 +85,6 @@ public class QuickWizardEntry {
cob = cobInfo.displayCob;
}
// Temp target
if (useTempTarget() == NO) {
tempTarget = null;
}
// Bolus IOB
boolean bolusIOB = false;
if (useBolusIOB() == YES) {
@ -130,8 +124,7 @@ public class QuickWizardEntry {
trend = true;
}
wizard.doCalc(profile, tempTarget, carbs(), cob, bg, 0d, bolusIOB, basalIOB, superBolus, trend);
return wizard;
return new BolusWizard(profile, profileName, tempTarget, carbs(), cob, bg, 0d, 100, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard");
}
public String buttonText() {

View file

@ -3,7 +3,7 @@ package info.nightscout.androidaps.db;
import android.content.Context;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
import com.j256.ormlite.dao.CloseableIterator;
@ -21,6 +21,8 @@ import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@ -29,6 +31,7 @@ import java.util.concurrent.TimeUnit;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.data.OverlappingIntervals;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.ProfileStore;
@ -410,6 +413,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
if (L.isEnabled(L.DATABASE))
log.debug("Firing EventNewBg");
MainApp.bus().post(new EventNewBG(bgReading));
RxBus.INSTANCE.send(new EventNewBG(bgReading));
scheduledBgPost = null;
}
}
@ -434,7 +438,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return null;
for (int i = 0; i < bgList.size(); i++)
if (bgList.get(i).value > 39)
if (bgList.get(i).value >= 39)
return bgList.get(i);
return null;
}
@ -534,6 +538,24 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return tddList;
}
public List<TDD> getTDDsForLastXDays(int days) {
List<TDD> tddList;
GregorianCalendar gc = new GregorianCalendar();
gc.add(Calendar.DAY_OF_YEAR, (-1) * days);
try {
QueryBuilder<TDD, String> queryBuilder = getDaoTDD().queryBuilder();
queryBuilder.orderBy("date", false);
Where<TDD, String> where = queryBuilder.where();
where.ge("date", gc.getTimeInMillis());
PreparedQuery<TDD> preparedQuery = queryBuilder.prepare();
tddList = getDaoTDD().query(preparedQuery);
} catch (SQLException e) {
log.error("Unhandled exception", e);
tddList = new ArrayList<>();
}
return tddList;
}
// ------------- DbRequests handling -------------------
@ -596,7 +618,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
}
}
// -------------------- TREATMENT HANDLING -------------------
// -------------------- TEMPTARGET HANDLING -------------------
public static void updateEarliestDataChange(long newDate) {
if (earliestDataChange == null) {
@ -627,6 +649,23 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return new ArrayList<TempTarget>();
}
public List<TempTarget> getTemptargetsDataFromTime(long from, long to, boolean ascending) {
try {
Dao<TempTarget, Long> daoTempTargets = getDaoTempTargets();
List<TempTarget> tempTargets;
QueryBuilder<TempTarget, Long> queryBuilder = daoTempTargets.queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.between("date", from, to);
PreparedQuery<TempTarget> preparedQuery = queryBuilder.prepare();
tempTargets = daoTempTargets.query(preparedQuery);
return tempTargets;
} catch (SQLException e) {
log.error("Unhandled exception", e);
}
return new ArrayList<TempTarget>();
}
public boolean createOrUpdate(TempTarget tempTarget) {
try {
TempTarget old;
@ -852,6 +891,31 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
log.debug("TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
return false;
}
// search by date (in case its standard record that has become pump record)
QueryBuilder<TemporaryBasal, Long> queryBuilder2 = getDaoTemporaryBasal().queryBuilder();
Where where2 = queryBuilder2.where();
where2.eq("date", tempBasal.date);
PreparedQuery<TemporaryBasal> preparedQuery2 = queryBuilder2.prepare();
List<TemporaryBasal> trList2 = getDaoTemporaryBasal().query(preparedQuery2);
if (trList2.size() > 0) {
old = trList2.get(0);
old.copyFromPump(tempBasal);
old.source = Source.PUMP;
if (L.isEnabled(L.DATABASE))
log.debug("TEMPBASAL: Updated record with Pump Data : " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
getDaoTemporaryBasal().update(old);
updateEarliestDataChange(tempBasal.date);
scheduleTemporaryBasalChange();
return false;
}
getDaoTemporaryBasal().create(tempBasal);
if (L.isEnabled(L.DATABASE))
log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
@ -950,6 +1014,22 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return new ArrayList<TemporaryBasal>();
}
public List<TemporaryBasal> getTemporaryBasalsDataFromTime(long from, long to, boolean ascending) {
try {
List<TemporaryBasal> tempbasals;
QueryBuilder<TemporaryBasal, Long> queryBuilder = getDaoTemporaryBasal().queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.between("date", from, to);
PreparedQuery<TemporaryBasal> preparedQuery = queryBuilder.prepare();
tempbasals = getDaoTemporaryBasal().query(preparedQuery);
return tempbasals;
} catch (SQLException e) {
log.error("Unhandled exception", e);
}
return new ArrayList<TemporaryBasal>();
}
private static void scheduleTemporaryBasalChange() {
class PostRunnable implements Runnable {
public void run() {
@ -1076,6 +1156,29 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return null;
}
public TemporaryBasal findTempBasalByPumpId(Long pumpId) {
try {
QueryBuilder<TemporaryBasal, Long> queryBuilder = null;
queryBuilder = getDaoTemporaryBasal().queryBuilder();
queryBuilder.orderBy("date", false);
Where where = queryBuilder.where();
where.eq("pumpId", pumpId);
PreparedQuery<TemporaryBasal> preparedQuery = queryBuilder.prepare();
List<TemporaryBasal> list = getDaoTemporaryBasal().query(preparedQuery);
if (list.size() > 0)
return list.get(0);
else
return null;
} catch (SQLException e) {
log.error("Unhandled exception", e);
}
return null;
}
// ------------ ExtendedBolus handling ---------------
public boolean createOrUpdate(ExtendedBolus extendedBolus) {
@ -1354,6 +1457,23 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return new ArrayList<>();
}
public List<CareportalEvent> getCareportalEvents(long start, long end, boolean ascending) {
try {
List<CareportalEvent> careportalEvents;
QueryBuilder<CareportalEvent, Long> queryBuilder = getDaoCareportalEvents().queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.between("date", start, end);
PreparedQuery<CareportalEvent> preparedQuery = queryBuilder.prepare();
careportalEvents = getDaoCareportalEvents().query(preparedQuery);
preprocessOpenAPSOfflineEvents(careportalEvents);
return careportalEvents;
} catch (SQLException e) {
log.error("Unhandled exception", e);
}
return new ArrayList<>();
}
public void preprocessOpenAPSOfflineEvents(List<CareportalEvent> list) {
OverlappingIntervals offlineEvents = new OverlappingIntervals();
for (int i = 0; i < list.size(); i++) {
@ -1507,6 +1627,24 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return new ArrayList<>();
}
public List<ProfileSwitch> getProfileSwitchEventsFromTime(long from, long to, boolean ascending) {
try {
Dao<ProfileSwitch, Long> daoProfileSwitch = getDaoProfileSwitch();
List<ProfileSwitch> profileSwitches;
QueryBuilder<ProfileSwitch, Long> queryBuilder = daoProfileSwitch.queryBuilder();
queryBuilder.orderBy("date", ascending);
queryBuilder.limit(100L);
Where where = queryBuilder.where();
where.between("date", from, to);
PreparedQuery<ProfileSwitch> preparedQuery = queryBuilder.prepare();
profileSwitches = daoProfileSwitch.query(preparedQuery);
return profileSwitches;
} catch (SQLException e) {
log.error("Unhandled exception", e);
}
return new ArrayList<>();
}
public boolean createOrUpdate(ProfileSwitch profileSwitch) {
try {
ProfileSwitch old;

View file

@ -0,0 +1,9 @@
package info.nightscout.androidaps.db;
public interface DbObjectBase {
long getDate();
long getPumpId();
}

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.db;
import android.graphics.Color;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;

View file

@ -6,9 +6,8 @@ import com.j256.ormlite.table.DatabaseTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil;
/**
* Created by mike on 20.09.2017.
@ -45,4 +44,16 @@ public class TDD {
this.basal = basal;
this.total = total;
}
@Override
public String toString() {
return "TDD [" +
"date=" + date +
"date(str)=" + DateTimeUtil.toStringFromTimeInMillis(date) +
", bolus=" + bolus +
", basal=" + basal +
", total=" + total +
']';
}
}

View file

@ -9,6 +9,9 @@ import org.slf4j.LoggerFactory;
import java.util.Objects;
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.Interval;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.utils.DateUtil;
@ -191,4 +194,11 @@ public class TempTarget implements Interval {
'}';
}
public String friendlyDescription(String units) {
return Profile.toTargetRangeString(low, high, Constants.MGDL, units) +
units +
"@" + MainApp.gs(R.string.mins, durationInMinutes) +
(reason != null && !reason.equals("") ? "(" + reason + ")" : "");
}
}

View file

@ -27,7 +27,7 @@ import info.nightscout.androidaps.utils.SP;
*/
@DatabaseTable(tableName = DatabaseHelper.DATABASE_TEMPORARYBASALS)
public class TemporaryBasal implements Interval {
public class TemporaryBasal implements Interval, DbObjectBase {
private static Logger log = LoggerFactory.getLogger(L.DATABASE);
@DatabaseField(id = true)
@ -156,6 +156,14 @@ public class TemporaryBasal implements Interval {
netExtendedRate = t.netExtendedRate;
}
public void copyFromPump(TemporaryBasal t) {
durationInMinutes = t.durationInMinutes;
isAbsolute = t.isAbsolute;
percentRate = t.percentRate;
absoluteRate = t.absoluteRate;
pumpId = t.pumpId;
}
// -------- Interval interface ---------
Long cuttedEnd = null;
@ -416,4 +424,13 @@ public class TemporaryBasal implements Interval {
}
}
@Override
public long getDate() {
return this.date;
}
@Override
public long getPumpId() {
return this.pumpId;
}
}

View file

@ -0,0 +1,4 @@
package info.nightscout.androidaps.events;
public class EventCustomActionsChanged extends Event {
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.events;
import android.location.Location;
public class EventLocationChange extends Event {
public Location location;
public EventLocationChange(Location location) {
this.location = location;
}
}

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.events;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import info.nightscout.androidaps.db.BgReading;

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.events;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import info.nightscout.androidaps.plugins.treatments.Treatment;

View file

@ -1,8 +1,11 @@
package info.nightscout.androidaps.interfaces;
import android.os.SystemClock;
import android.support.v4.app.FragmentActivity;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.SP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -42,6 +45,28 @@ public abstract class PluginBase {
pluginSwitcher.invoke();
}
protected void confirmPumpPluginActivation(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity activity) {
boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false);
if (allowHardwarePump || activity == null) {
pluginSwitcher.invoke();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(R.string.allow_hardware_pump_text)
.setPositiveButton(R.string.yes, (dialog, id) -> {
pluginSwitcher.invoke();
SP.putBoolean("allow_hardware_pump", true);
if (L.isEnabled(L.PUMP))
log.debug("First time HW pump allowed!");
})
.setNegativeButton(R.string.cancel, (dialog, id) -> {
pluginSwitcher.cancel();
if (L.isEnabled(L.PUMP))
log.debug("User does not allow switching to HW pump!");
});
builder.create().show();
}
}
// public PluginType getType() {
// return mainType;
// }

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.interfaces;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import info.nightscout.androidaps.data.ProfileStore;

View file

@ -7,8 +7,10 @@ import java.util.List;
import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.plugins.common.ManufacturerType;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
/**
* Created by mike on 04.06.2016.
@ -53,7 +55,9 @@ public interface PumpInterface {
// Status to be passed to NS
JSONObject getJSONStatus(Profile profile, String profileName);
String deviceID();
ManufacturerType manufacturer();
PumpType model();
String serialNumber();
// Pump capabilities
PumpDescription getPumpDescription();
@ -71,4 +75,10 @@ public interface PumpInterface {
void executeCustomAction(CustomActionType customActionType);
/**
* This method will be called when time or Timezone changes, and pump driver can then do a specific action (for
* example update clock on pump).
*/
void timeDateOrTimeZoneChanged();
}

View file

@ -5,6 +5,7 @@ import java.util.List;
import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
import info.nightscout.androidaps.data.NonOverlappingIntervals;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.ProfileSwitch;
@ -31,6 +32,7 @@ public interface TreatmentsInterface {
List<Treatment> getTreatmentsFromHistory();
List<Treatment> getTreatments5MinBackFromHistory(long time);
List<Treatment> getTreatmentsFromHistoryAfterTimestamp(long timestamp);
long getLastBolusTime();
// real basals (not faked by extended bolus)
@ -42,7 +44,7 @@ public interface TreatmentsInterface {
// basal that can be faked by extended boluses
boolean isTempBasalInProgress();
TemporaryBasal getTempBasalFromHistory(long time);
Intervals<TemporaryBasal> getTemporaryBasalsFromHistory();
NonOverlappingIntervals<TemporaryBasal> getTemporaryBasalsFromHistory();
boolean isInHistoryExtendedBoluslInProgress();
ExtendedBolus getExtendedBolusFromHistory(long time);
@ -63,4 +65,4 @@ public interface TreatmentsInterface {
long oldestDataAvailable();
}
}

View file

@ -77,6 +77,7 @@ public class L {
public static final String CORE = "CORE";
public static final String AUTOSENS = "AUTOSENS";
public static final String AUTOMATION = "AUTOMATION";
public static final String EVENTS = "EVENTS";
public static final String GLUCOSE = "GLUCOSE";
public static final String BGSOURCE = "BGSOURCE";
@ -87,6 +88,7 @@ public class L {
public static final String DATAFOOD = "DATAFOOD";
public static final String DATATREATMENTS = "DATATREATMENTS";
public static final String NSCLIENT = "NSCLIENT";
public static final String TIDEPOOL = "TIDEPOOL";
public static final String CONSTRAINTS = "CONSTRAINTS";
public static final String PUMP = "PUMP";
public static final String PUMPQUEUE = "PUMPQUEUE";
@ -96,11 +98,13 @@ public class L {
public static final String PROFILE = "PROFILE";
public static final String CONFIGBUILDER = "CONFIGBUILDER";
public static final String UI = "UI";
public static final String LOCATION = "LOCATION";
public static final String SMS = "SMS";
private static void initialize() {
logElements = new ArrayList<>();
logElements.add(new LogElement(APS, true));
logElements.add(new LogElement(AUTOMATION, true));
logElements.add(new LogElement(AUTOSENS, false));
logElements.add(new LogElement(BGSOURCE, true));
logElements.add(new LogElement(GLUCOSE, false));
@ -112,8 +116,10 @@ public class L {
logElements.add(new LogElement(DATASERVICE, true));
logElements.add(new LogElement(DATATREATMENTS, true));
logElements.add(new LogElement(EVENTS, false, true));
logElements.add(new LogElement(LOCATION, true));
logElements.add(new LogElement(NOTIFICATION, true));
logElements.add(new LogElement(NSCLIENT, true));
logElements.add(new LogElement(TIDEPOOL, true));
logElements.add(new LogElement(OVERVIEW, true));
logElements.add(new LogElement(PROFILE, true));
logElements.add(new LogElement(PUMP, true));

View file

@ -10,8 +10,8 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import com.squareup.otto.Subscribe;
@ -644,14 +644,27 @@ public class LoopPlugin extends PluginBase {
TreatmentsInterface activeTreatments = TreatmentsPlugin.getPlugin();
LoopPlugin.getPlugin().disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L);
ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalPercent(0, durationInMinutes, true, profile, new Callback() {
@Override
public void run() {
if (!result.success) {
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.tempbasaldeliveryerror));
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalAbsolute(0, durationInMinutes, true, profile, new Callback() {
@Override
public void run() {
if (!result.success) {
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.tempbasaldeliveryerror));
}
}
}
});
});
} else {
ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalPercent(0, durationInMinutes, true, profile, new Callback() {
@Override
public void run() {
if (!result.success) {
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.tempbasaldeliveryerror));
}
}
});
}
if (pump.getPumpDescription().isExtendedBolusCapable && activeTreatments.isInHistoryExtendedBoluslInProgress()) {
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelExtended(new Callback() {
@Override

View file

@ -0,0 +1,23 @@
package info.nightscout.androidaps.plugins.bus
import info.nightscout.androidaps.events.Event
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject
// Use object so we have a singleton instance
object RxBus {
private val publisher = PublishSubject.create<Event>()
fun send(event: Event) {
publisher.onNext(event)
}
// Listen should return an Observable and not the publisher
// Using ofType we filter only events that match that class type
fun <T> toObservable(eventType: Class<T>): Observable<T> =
publisher
.subscribeOn(Schedulers.io())
.ofType(eventType)
}

View file

@ -0,0 +1,27 @@
package info.nightscout.androidaps.plugins.common;
public enum ManufacturerType {
AndroidAPS("AndroidAPS"),
Medtronic("Medtronic"),
Sooil("SOOIL"),
Tandem("Tandem"),
Insulet("Insulet"),
Animas("Animas"), Cellnovo("Cellnovo"), Roche("Roche");
private String description;
ManufacturerType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.common;
import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
import butterknife.Unbinder;
import info.nightscout.androidaps.MainApp;

View file

@ -3,8 +3,8 @@ package info.nightscout.androidaps.plugins.configBuilder;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.configBuilder;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -232,26 +232,32 @@ public class ConfigBuilderPlugin extends PluginBase {
return commandQueue;
}
@Nullable
public BgSourceInterface getActiveBgSource() {
return activeBgSource;
}
@Nullable
public ProfileInterface getActiveProfileInterface() {
return activeProfile;
}
@Nullable
public InsulinInterface getActiveInsulin() {
return activeInsulin;
}
@Nullable
public APSInterface getActiveAPS() {
return activeAPS;
}
@Nullable
public PumpInterface getActivePump() {
return activePump;
}
@Nullable
public SensitivityInterface getActiveSensitivity() {
return activeSensitivity;
}

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.configBuilder;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -2,7 +2,7 @@ package info.nightscout.androidaps.plugins.configBuilder;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import com.google.firebase.analytics.FirebaseAnalytics;
import com.squareup.otto.Subscribe;

View file

@ -4,11 +4,11 @@ import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import android.support.annotation.StringRes;
import androidx.annotation.StringRes;
import java.util.ArrayList;
import java.util.Date;

View file

@ -13,10 +13,10 @@ import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin;
import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin;
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
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;
@ -196,7 +196,7 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface {
PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (pump != null) {
double rounded = Round.roundTo(insulin.value(), pump.getPumpDescription().pumpType.determineCorrectBolusSize(insulin.value()));
double rounded = pump.getPumpDescription().pumpType.determineCorrectBolusSize(insulin.value());
insulin.setIfDifferent(rounded, MainApp.gs(R.string.pumplimit), this);
}
return insulin;
@ -213,7 +213,7 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface {
PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (pump != null) {
double rounded = Round.roundTo(insulin.value(), pump.getPumpDescription().pumpType.determineCorrectExtendedBolusSize(insulin.value()));
double rounded = pump.getPumpDescription().pumpType.determineCorrectExtendedBolusSize(insulin.value());
insulin.setIfDifferent(rounded, MainApp.gs(R.string.pumplimit), this);
}
return insulin;

View file

@ -5,8 +5,8 @@ import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -26,6 +26,7 @@ import info.nightscout.androidaps.activities.HistoryBrowseActivity;
import info.nightscout.androidaps.activities.TDDStatsActivity;
import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventCustomActionsChanged;
import info.nightscout.androidaps.events.EventExtendedBolusChange;
import info.nightscout.androidaps.events.EventInitializationChanged;
import info.nightscout.androidaps.events.EventRefreshOverview;
@ -133,6 +134,11 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
updateGUI();
}
@Subscribe
public void onStatusEvent(final EventCustomActionsChanged ev) {
updateGUI();
}
@Override
protected void updateGUI() {
Activity activity = getActivity();
@ -247,6 +253,9 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
for (CustomAction customAction : customActions) {
if (!customAction.isEnabled())
continue;
SingleClickButton btn = new SingleClickButton(getContext(), null, android.R.attr.buttonStyle);
btn.setText(MainApp.gs(customAction.getName()));
@ -264,10 +273,8 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
this.pumpCustomActions.put(MainApp.gs(customAction.getName()), customAction);
this.pumpCustomButtons.add(btn);
}
}
}
@ -283,7 +290,6 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
}
pumpCustomButtons.clear();
pumpCustomActions.clear();
}
@ -335,4 +341,4 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
break;
}
}
}
}

View file

@ -12,31 +12,36 @@ public class CustomAction {
private String iconName;
private CustomActionType customActionType;
private int iconResourceId;
private boolean enabled = true;
public CustomAction(int nameResourceId, CustomActionType actionType) {
this.name = nameResourceId;
this.customActionType = actionType;
this.iconResourceId = R.drawable.icon_actions_profileswitch;
this(nameResourceId, actionType, R.drawable.icon_actions_profileswitch, true);
}
public CustomAction(int nameResourceId, CustomActionType actionType, int iconResourceId) {
this(nameResourceId, actionType, iconResourceId, true);
}
public CustomAction(int nameResourceId, CustomActionType actionType, boolean enabled) {
this(nameResourceId, actionType, R.drawable.icon_actions_profileswitch, enabled);
}
public CustomAction(int nameResourceId, CustomActionType actionType, int iconResourceId, boolean enabled) {
this.name = nameResourceId;
this.customActionType = actionType;
this.iconResourceId = iconResourceId;
this.enabled = enabled;
}
public int getName() {
return name;
}
public CustomActionType getCustomActionType() {
return customActionType;
}
@ -44,4 +49,15 @@ public class CustomAction {
public int getIconResourceId() {
return iconResourceId;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View file

@ -3,8 +3,8 @@ package info.nightscout.androidaps.plugins.general.actions.dialogs;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.appcompat.app.AlertDialog;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
@ -103,7 +103,7 @@ public class FillDialog extends DialogFragment implements OnClickListener {
Double maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
double bolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep;
editInsulin = view.findViewById(R.id.fill_insulinamount);
editInsulin.setParams(0d, 0d, maxInsulin, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false, textWatcher);
editInsulin.setParams(0d, 0d, maxInsulin, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false, view.findViewById(R.id.ok), textWatcher);
Button preset1Button = view.findViewById(R.id.fill_preset_button1);
@ -184,7 +184,7 @@ public class FillDialog extends DialogFragment implements OnClickListener {
confirmMessage.add("");
confirmMessage.add(MainApp.gs(R.string.bolus) + ": " + "<font color='" + MainApp.gc(R.color.colorCarbsButton) + "'>" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + "</font>");
if (Math.abs(insulinAfterConstraints - insulin) > 0.01d)
confirmMessage.add("<font color='" + MainApp.gc(R.color.low) + "'>" + MainApp.gs(R.string.bolusconstraintapplied) + "</font>");
confirmMessage.add(MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints));
}
if (pumpSiteChangeCheckbox.isChecked())

View file

@ -4,8 +4,8 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.appcompat.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -42,12 +42,12 @@ public class NewExtendedBolusDialog extends DialogFragment implements View.OnCli
Double maxInsulin = MainApp.getConstraintChecker().getMaxExtendedBolusAllowed().value();
editInsulin = (NumberPicker) view.findViewById(R.id.overview_newextendedbolus_insulin);
editInsulin.setParams(0d, 0d, maxInsulin, 0.1d, new DecimalFormat("0.00"), false);
editInsulin.setParams(0d, 0d, maxInsulin, 0.1d, new DecimalFormat("0.00"), false, view.findViewById(R.id.ok));
double extendedDurationStep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().extendedBolusDurationStep;
double extendedMaxDuration = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().extendedBolusMaxDuration;
editDuration = (NumberPicker) view.findViewById(R.id.overview_newextendedbolus_duration);
editDuration.setParams(extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, new DecimalFormat("0"), false);
editDuration.setParams(extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
view.findViewById(R.id.ok).setOnClickListener(this);
view.findViewById(R.id.cancel).setOnClickListener(this);

View file

@ -3,8 +3,8 @@ package info.nightscout.androidaps.plugins.general.actions.dialogs;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.appcompat.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -66,17 +66,17 @@ public class NewTempBasalDialog extends DialogFragment implements View.OnClickLi
basalPercent = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_basalpercentinput);
double maxTempPercent = pumpDescription.maxTempPercent;
double tempPercentStep = pumpDescription.tempPercentStep;
basalPercent.setParams(100d, 0d, maxTempPercent, tempPercentStep, new DecimalFormat("0"), true);
basalPercent.setParams(100d, 0d, maxTempPercent, tempPercentStep, new DecimalFormat("0"), true, view.findViewById(R.id.ok));
Profile profile = ProfileFunctions.getInstance().getProfile();
Double currentBasal = profile != null ? profile.getBasal() : 0d;
basalAbsolute = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_basalabsoluteinput);
basalAbsolute.setParams(currentBasal, 0d, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, new DecimalFormat("0.00"), true);
basalAbsolute.setParams(currentBasal, 0d, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, new DecimalFormat("0.00"), true, view.findViewById(R.id.ok));
double tempDurationStep = pumpDescription.tempDurationStep;
double tempMaxDuration = pumpDescription.tempMaxDuration;
duration = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_duration);
duration.setParams(tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, new DecimalFormat("0"), false);
duration.setParams(tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
if ((pumpDescription.tempBasalStyle & PumpDescription.PERCENT) == PumpDescription.PERCENT && (pumpDescription.tempBasalStyle & PumpDescription.ABSOLUTE) == PumpDescription.ABSOLUTE) {
// Both allowed

View file

@ -0,0 +1,90 @@
package info.nightscout.androidaps.plugins.general.automation;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
public class AutomationEvent {
private Trigger trigger = new TriggerConnector();
private List<Action> actions = new ArrayList<>();
private String title;
public void setTitle(String title) {
this.title = title;
}
public void setTrigger(Trigger trigger) {
this.trigger = trigger;
}
public Trigger getTrigger() {
return trigger;
}
public List<Action> getActions() {
return actions;
}
public TriggerConnector getPreconditions() {
TriggerConnector trigger = new TriggerConnector(TriggerConnector.Type.AND);
for (Action action : actions) {
if (action.precondition != null)
trigger.add(action.precondition);
}
return trigger;
}
public void addAction(Action action) {
actions.add(action);
}
public String getTitle() {
return title;
}
public String toJSON() {
JSONObject o = new JSONObject();
try {
// title
o.put("title", title);
// trigger
o.put("trigger", trigger.toJSON());
// actions
JSONArray array = new JSONArray();
for (Action a : actions) {
array.put(a.toJSON());
}
o.put("actions", array);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
public AutomationEvent fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
// title
title = d.optString("title", "");
// trigger
trigger = Trigger.instantiate(d.getString("trigger"));
// actions
JSONArray array = d.getJSONArray("actions");
actions.clear();
for (int i = 0; i < array.length(); i++) {
actions.add(Action.instantiate(new JSONObject(array.getString(i))));
}
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
}

View file

@ -0,0 +1,73 @@
package info.nightscout.androidaps.plugins.general.automation
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.automation_fragment.*
class AutomationFragment : Fragment() {
private var disposable: CompositeDisposable = CompositeDisposable()
private var eventListAdapter: EventListAdapter? = null
operator fun CompositeDisposable.plusAssign(disposable: Disposable) {
add(disposable)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.automation_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
eventListAdapter = EventListAdapter(AutomationPlugin.automationEvents, fragmentManager)
automation_eventListView.layoutManager = LinearLayoutManager(context)
automation_eventListView.adapter = eventListAdapter
automation_fabAddEvent.setOnClickListener {
val dialog = EditEventDialog()
val args = Bundle()
args.putString("event", AutomationEvent().toJSON())
args.putInt("position", -1) // New event
dialog.arguments = args
fragmentManager?.let { dialog.show(it, "EditEventDialog") }
}
disposable += RxBus
.toObservable(EventAutomationUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
eventListAdapter?.notifyDataSetChanged()
val sb = StringBuilder()
for (l in AutomationPlugin.executionLog) {
sb.append(l)
sb.append("\n")
}
automation_logView.text = sb.toString()
}, {})
disposable += RxBus
.toObservable(EventAutomationDataChanged::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
eventListAdapter?.notifyDataSetChanged()
}, {})
}
override fun onStop() {
super.onStop()
disposable.clear()
}
}

View file

@ -0,0 +1,213 @@
package info.nightscout.androidaps.plugins.general.automation
import android.content.Intent
import android.os.Handler
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventChargingState
import info.nightscout.androidaps.events.EventLocationChange
import info.nightscout.androidaps.events.EventNetworkChange
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.actions.*
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import info.nightscout.androidaps.plugins.general.automation.triggers.*
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.services.LocationService
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.SP
import info.nightscout.androidaps.utils.T
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.util.*
object AutomationPlugin : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(AutomationFragment::class.qualifiedName)
.pluginName(R.string.automation)
.shortName(R.string.automation_short)
.preferencesId(R.xml.pref_automation)
.description(R.string.automation_description)) {
private val log = LoggerFactory.getLogger(L.AUTOMATION)
private var disposable: CompositeDisposable = CompositeDisposable()
private const val key_AUTOMATION_EVENTS = "AUTOMATION_EVENTS"
val automationEvents = ArrayList<AutomationEvent>()
var executionLog: MutableList<String> = ArrayList()
private val loopHandler = Handler()
private lateinit var refreshLoop: Runnable
init {
refreshLoop = Runnable {
processActions()
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs())
}
}
operator fun CompositeDisposable.plusAssign(disposable: Disposable) {
add(disposable)
}
override fun onStart() {
val context = MainApp.instance().applicationContext
context.startService(Intent(context, LocationService::class.java))
super.onStart()
loadFromSP()
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs())
disposable += RxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ e ->
if (e.isChanged(R.string.key_location)) {
val ctx = MainApp.instance().applicationContext
ctx.stopService(Intent(ctx, LocationService::class.java))
ctx.startService(Intent(ctx, LocationService::class.java))
}
}, {})
disposable += RxBus
.toObservable(EventAutomationDataChanged::class.java)
.observeOn(Schedulers.io())
.subscribe({ storeToSP() }, {})
disposable += RxBus
.toObservable(EventLocationChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ e ->
e?.let {
log.debug("Grabbed location: $it.location.latitude $it.location.longitude Provider: $it.location.provider")
processActions()
}
}, {})
disposable += RxBus
.toObservable(EventChargingState::class.java)
.observeOn(Schedulers.io())
.subscribe({ processActions() }, {})
disposable += RxBus
.toObservable(EventNetworkChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ processActions() }, {})
disposable += RxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(Schedulers.io())
.subscribe({ processActions() }, {})
}
override fun onStop() {
disposable.clear()
loopHandler.removeCallbacks(refreshLoop)
val context = MainApp.instance().applicationContext
context.stopService(Intent(context, LocationService::class.java))
}
private fun storeToSP() {
val array = JSONArray()
try {
for (event in automationEvents) {
array.put(JSONObject(event.toJSON()))
}
} catch (e: JSONException) {
e.printStackTrace()
}
SP.putString(key_AUTOMATION_EVENTS, array.toString())
}
private fun loadFromSP() {
automationEvents.clear()
val data = SP.getString(key_AUTOMATION_EVENTS, "")
if (data != "") {
try {
val array = JSONArray(data)
for (i in 0 until array.length()) {
val o = array.getJSONObject(i)
val event = AutomationEvent().fromJSON(o.toString())
automationEvents.add(event)
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
}
@Synchronized
private fun processActions() {
if (!isEnabled(PluginType.GENERAL))
return
if (L.isEnabled(L.AUTOMATION))
log.debug("processActions")
for (event in automationEvents) {
if (event.trigger.shouldRun() && event.preconditions.shouldRun()) {
val actions = event.actions
for (action in actions) {
action.doAction(object : Callback() {
override fun run() {
val sb = StringBuilder()
sb.append(DateUtil.timeString(DateUtil.now()))
sb.append(" ")
sb.append(if (result.success) "" else "X")
sb.append(" ")
sb.append(event.title)
sb.append(": ")
sb.append(action.shortDescription())
sb.append(": ")
sb.append(result.comment)
executionLog.add(sb.toString())
if (L.isEnabled(L.AUTOMATION))
log.debug("Executed: $sb")
RxBus.send(EventAutomationUpdateGui())
}
})
}
event.trigger.executed(DateUtil.now())
}
}
storeToSP() // save last run time
}
fun getActionDummyObjects(): List<Action> {
return listOf(
//ActionLoopDisable(),
//ActionLoopEnable(),
//ActionLoopResume(),
//ActionLoopSuspend(),
ActionStartTempTarget(),
ActionStopTempTarget(),
ActionNotification(),
ActionProfileSwitchPercent()
)
}
fun getTriggerDummyObjects(): List<Trigger> {
return listOf(
TriggerTime(),
TriggerRecurringTime(),
TriggerBg(),
TriggerDelta(),
TriggerIob(),
TriggerCOB(),
TriggerProfilePercent(),
TriggerTempTarget(),
TriggerWifiSsid(),
TriggerLocation(),
TriggerAutosensValue(),
TriggerBolusAgo()
)
}
}

View file

@ -0,0 +1,120 @@
package info.nightscout.androidaps.plugins.general.automation;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.HashSet;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
private final List<AutomationEvent> mEventList;
private final FragmentManager mFragmentManager;
EventListAdapter(List<AutomationEvent> events, FragmentManager fragmentManager) {
this.mEventList = events;
this.mFragmentManager = fragmentManager;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.automation_event_item, parent, false);
return new ViewHolder(v, parent.getContext());
}
private void addImage(@DrawableRes int res, Context context, LinearLayout layout) {
ImageView iv = new ImageView(context);
iv.setImageResource(res);
iv.setLayoutParams(new LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)));
layout.addView(iv);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
final AutomationEvent event = mEventList.get(position);
holder.eventTitle.setText(event.getTitle());
holder.iconLayout.removeAllViews();
// trigger icons
HashSet<Integer> triggerIcons = new HashSet<>();
TriggerConnector.fillIconSet((TriggerConnector) event.getTrigger(), triggerIcons);
for (int res : triggerIcons) {
addImage(res, holder.context, holder.iconLayout);
}
// arrow icon
ImageView iv = new ImageView(holder.context);
iv.setImageResource(R.drawable.ic_arrow_forward_white_24dp);
iv.setLayoutParams(new LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)));
iv.setPadding(MainApp.dpToPx(4), 0, MainApp.dpToPx(4), 0);
holder.iconLayout.addView(iv);
// action icons
HashSet<Integer> actionIcons = new HashSet<>();
for (Action action : event.getActions()) {
if (action.icon().isPresent())
actionIcons.add(action.icon().get());
}
for (int res : actionIcons) {
addImage(res, holder.context, holder.iconLayout);
}
// remove event
holder.iconTrash.setOnClickListener(v -> {
mEventList.remove(event);
notifyDataSetChanged();
});
// edit event
holder.rootLayout.setOnClickListener(v -> {
//EditEventDialog dialog = EditEventDialog.Companion.newInstance(event, false);
EditEventDialog dialog = new EditEventDialog();
Bundle args = new Bundle();
args.putString("event", event.toJSON());
args.putInt("position", position);
dialog.setArguments(args);
if (mFragmentManager != null)
dialog.show(mFragmentManager, "EditEventDialog");
});
}
@Override
public int getItemCount() {
return mEventList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
final RelativeLayout rootLayout;
final LinearLayout iconLayout;
final TextView eventTitle;
final Context context;
final ImageView iconTrash;
ViewHolder(View view, Context context) {
super(view);
this.context = context;
eventTitle = view.findViewById(R.id.viewEventTitle);
rootLayout = view.findViewById(R.id.rootLayout);
iconLayout = view.findViewById(R.id.iconLayout);
iconTrash = view.findViewById(R.id.iconTrash);
}
}
}

View file

@ -0,0 +1,104 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import android.widget.LinearLayout;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import javax.annotation.Nullable;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.queue.Callback;
/*
Action ideas:
* cancel temp target
* change preference setting
* enable/disable plugin
* create notification
* create ugly alarm
* create profile switch
* set/cancel tbr
* set/cancel extended bolus
* run bolus wizard
Trigger ideas:
* location (close to)
* connected to specific wifi
* internet available/not available
* nsclient connected/disconnected
* iob
* cob
* autosens value
* delta, short delta, long delta
* last bolus ago
* is tbr running
* bolus wizard result
* loop is enabled, disabled, suspended, running
*/
public abstract class Action {
public Trigger precondition = null;
public abstract int friendlyName();
public abstract String shortDescription();
public abstract void doAction(Callback callback);
public void generateDialog(LinearLayout root) {
}
public boolean hasDialog() {
return false;
}
public String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", this.getClass().getName());
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
public abstract Optional<Integer> icon();
public Action fromJSON(String data) {
return this;
}
@Nullable
public static Action instantiate(JSONObject object) {
try {
String type = object.getString("type");
JSONObject data = object.optJSONObject("data");
Class clazz = Class.forName(type);
return ((Action) clazz.newInstance()).fromJSON(data != null ? data.toString() : "");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
e.printStackTrace();
}
return null;
}
public void apply(Action a) {
try {
JSONObject object = new JSONObject(a.toJSON());
String type = object.getString("type");
JSONObject data = object.getJSONObject("data");
if (type.equals(getClass().getName())) {
fromJSON(data.toString());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,49 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import com.google.common.base.Optional;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.queue.Callback;
public class ActionLoopDisable extends Action {
@Override
public int friendlyName() {
return R.string.disableloop;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.disableloop);
}
@Override
public void doAction(Callback callback) {
if (LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, false);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopDisable");
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() {
@Override
public void run() {
RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopDisable"));
if (callback != null)
callback.result(result).run();
}
});
} else {
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadydisabled)).run();
}
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_stop_24dp);
}
}

View file

@ -0,0 +1,44 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import com.google.common.base.Optional;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.queue.Callback;
public class ActionLoopEnable extends Action {
@Override
public int friendlyName() {
return R.string.enableloop;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.enableloop);
}
@Override
public void doAction(Callback callback) {
if (!LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, true);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopEnable");
RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopEnable"));
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
} else {
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadyenabled)).run();
}
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_play_circle_outline_24dp);
}
}

View file

@ -0,0 +1,45 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import com.google.common.base.Optional;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.queue.Callback;
public class ActionLoopResume extends Action {
@Override
public int friendlyName() {
return R.string.resumeloop;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.resumeloop);
}
@Override
public void doAction(Callback callback) {
if (LoopPlugin.getPlugin().isSuspended()) {
LoopPlugin.getPlugin().suspendTo(0);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopResume");
NSUpload.uploadOpenAPSOffline(0);
RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopResume"));
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
} else {
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.notsuspended)).run();
}
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_replay_24dp);
}
}

View file

@ -0,0 +1,91 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import android.widget.LinearLayout;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionLoopSuspend extends Action {
public InputDuration minutes = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
@Override
public int friendlyName() {
return R.string.suspendloop;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.suspendloopforXmin, minutes.getMinutes());
}
@Override
public void doAction(Callback callback) {
if (!LoopPlugin.getPlugin().isSuspended()) {
LoopPlugin.getPlugin().suspendLoop(minutes.getMinutes());
RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopSuspend"));
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
} else {
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadysuspended)).run();
}
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_pause_circle_outline_24dp);
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
JSONObject data = new JSONObject();
try {
data.put("minutes", minutes.getMinutes());
o.put("type", this.getClass().getName());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject o = new JSONObject(data);
minutes.setMinutes(JsonHelper.safeGetInt(o, "minutes"));
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public boolean hasDialog() {
return true;
}
@Override
public void generateDialog(LinearLayout root) {
new LayoutBuilder()
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", minutes))
.build(root);
}
}

View file

@ -0,0 +1,91 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import android.widget.LinearLayout;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.automation.elements.InputString;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionNotification extends Action {
public InputString text = new InputString();
@Override
public int friendlyName() {
return R.string.notification;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.notification_message, text.getValue());
}
@Override
public void doAction(Callback callback) {
Notification notification = new Notification(Notification.USERMESSAGE, text.getValue(), Notification.URGENT);
RxBus.INSTANCE.send(new EventNewNotification(notification));
NSUpload.uploadError(text.getValue());
RxBus.INSTANCE.send(new EventRefreshOverview("ActionNotification"));
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_notifications);
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
JSONObject data = new JSONObject();
try {
data.put("text", text.getValue());
o.put("type", this.getClass().getName());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject o = new JSONObject(data);
text.setValue(JsonHelper.safeGetString(o, "text"));
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public boolean hasDialog() {
return true;
}
@Override
public void generateDialog(LinearLayout root) {
new LayoutBuilder()
.add(new LabelWithElement(MainApp.gs(R.string.message_short), "", text))
.build(root);
}
}

View file

@ -0,0 +1,95 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import android.widget.LinearLayout;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
import info.nightscout.androidaps.plugins.general.automation.elements.InputPercent;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerProfilePercent;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionProfileSwitchPercent extends Action {
InputPercent pct = new InputPercent();
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
public ActionProfileSwitchPercent() {
precondition = new TriggerProfilePercent().comparator(Comparator.Compare.IS_EQUAL).setValue(100);
}
@Override
public int friendlyName() {
return R.string.profilepercentage;
}
@Override
public String shortDescription() {
if (duration.getMinutes() == 0)
return MainApp.gs(R.string.startprofileforever, (int) pct.getValue());
else
return MainApp.gs(R.string.startprofile, (int) pct.getValue(), duration.getMinutes());
}
@Override
public void doAction(Callback callback) {
ProfileFunctions.doProfileSwitch((int) duration.getValue(), (int) pct.getValue(), 0);
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
}
@Override
public void generateDialog(LinearLayout root) {
new LayoutBuilder()
.add(new LabelWithElement(MainApp.gs(R.string.percent_u), "", pct))
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", duration))
.build(root);
}
@Override
public boolean hasDialog() {
return true;
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", ActionProfileSwitchPercent.class.getName());
JSONObject data = new JSONObject();
data.put("percentage", pct.getValue());
data.put("durationInMinutes", duration.getMinutes());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
pct.setValue(JsonHelper.safeGetInt(d, "percentage"));
duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes"));
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_actions_profileswitch);
}
}

View file

@ -0,0 +1,119 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import android.widget.LinearLayout;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
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.PumpEnactResult;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.plugins.general.automation.elements.ComparatorExists;
import info.nightscout.androidaps.plugins.general.automation.elements.InputBg;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTempTarget;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionStartTempTarget extends Action {
String reason = "";
InputBg value = new InputBg();
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
private TempTarget tempTarget;
public ActionStartTempTarget() {
precondition = new TriggerTempTarget().comparator(ComparatorExists.Compare.NOT_EXISTS);
}
@Override
public int friendlyName() {
return R.string.starttemptarget;
}
@Override
public String shortDescription() {
tempTarget = new TempTarget()
.date(DateUtil.now())
.duration(duration.getMinutes())
.reason(reason)
.source(Source.USER)
.low(Profile.toMgdl(value.getValue(), value.getUnits()))
.high(Profile.toMgdl(value.getValue(), value.getUnits()));
return MainApp.gs(R.string.starttemptarget) + ": " + (tempTarget == null ? "null" : tempTarget.friendlyDescription(value.getUnits()));
}
@Override
public void doAction(Callback callback) {
tempTarget = new TempTarget()
.date(DateUtil.now())
.duration(duration.getMinutes())
.reason(reason)
.source(Source.USER)
.low(Profile.toMgdl(value.getValue(), value.getUnits()))
.high(Profile.toMgdl(value.getValue(), value.getUnits()));
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
}
@Override
public void generateDialog(LinearLayout root) {
int unitResId = value.getUnits().equals(Constants.MGDL) ? R.string.mgdl : R.string.mmol;
new LayoutBuilder()
.add(new LabelWithElement(MainApp.gs(R.string.careportal_temporarytarget), MainApp.gs(unitResId), value))
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", duration))
.build(root);
}
@Override
public boolean hasDialog() {
return true;
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", ActionStartTempTarget.class.getName());
JSONObject data = new JSONObject();
data.put("reason", reason);
data.put("value", value.getValue());
data.put("units", value.getUnits());
data.put("durationInMinutes", duration.getMinutes());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
reason = JsonHelper.safeGetString(d, "reason");
value.setUnits(JsonHelper.safeGetString(d, "units"));
value.setValue(JsonHelper.safeGetDouble(d, "value"));
duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes"));
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_cp_cgm_target);
}
}

View file

@ -0,0 +1,77 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionStopTempTarget extends Action {
String reason = "";
private TempTarget tempTarget;
public ActionStopTempTarget() {
}
@Override
public int friendlyName() {
return R.string.stoptemptarget;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.stoptemptarget);
}
@Override
public void doAction(Callback callback) {
tempTarget = new TempTarget().date(DateUtil.now()).duration(0).reason(reason).source(Source.USER).low(0).high(0);
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
}
@Override
public boolean hasDialog() {
return false;
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", ActionStopTempTarget.class.getName());
JSONObject data = new JSONObject();
data.put("reason", reason);
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
reason = JsonHelper.safeGetString(d, "reason");
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_stop_24dp);
}
}

View file

@ -0,0 +1,54 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.actions.Action
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
class ActionListAdapter(private val fragmentManager: FragmentManager, private val actionList: MutableList<Action>) : RecyclerView.Adapter<ActionListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.automation_action_item, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val action = actionList[position]
holder.bind(action, fragmentManager, this, position, actionList)
}
override fun getItemCount(): Int {
return actionList.size
}
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(action: Action, fragmentManager: FragmentManager, recyclerView: RecyclerView.Adapter<ViewHolder>, position : Int, actionList: MutableList<Action>) {
view.findViewById<LinearLayout>(R.id.automation_layoutText).setOnClickListener {
if (action.hasDialog()) {
val args = Bundle()
args.putInt("actionPosition", position)
args.putString("action", action.toJSON())
val dialog = EditActionDialog()
dialog.arguments = args
dialog.show(fragmentManager, "EditActionDialog")
}
}
view.findViewById<ImageView>(R.id.automation_iconTrash).setOnClickListener {
actionList.remove(action)
recyclerView.notifyDataSetChanged()
RxBus.send(EventAutomationUpdateGui())
}
view.findViewById<TextView>(R.id.automation_viewActionTitle).text = action.shortDescription()
}
}
}

View file

@ -0,0 +1,85 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RadioButton
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.automation.actions.Action
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationAddAction
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import kotlinx.android.synthetic.main.automation_dialog_choose_action.*
import kotlinx.android.synthetic.main.okcancel.*
class ChooseActionDialog : DialogFragment() {
var checkedIndex = -1
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// restore checked radio button
savedInstanceState?.let { bundle ->
checkedIndex = bundle.getInt("checkedIndex")
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_choose_action, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
for (a in AutomationPlugin.getActionDummyObjects()) {
val radioButton = RadioButton(context)
radioButton.setText(a.friendlyName())
radioButton.tag = a.javaClass
automation_radioGroup.addView(radioButton)
}
if (checkedIndex != -1)
(automation_radioGroup.getChildAt(checkedIndex) as RadioButton).isChecked = true
// OK button
ok.setOnClickListener {
dismiss()
instantiateAction()?.let {
RxBus.send(EventAutomationAddAction(it))
RxBus.send(EventAutomationUpdateGui())
}
}
// Cancel button
cancel.setOnClickListener { dismiss() }
}
override fun onSaveInstanceState(bundle: Bundle) {
bundle.putInt("checkedIndex", determineCheckedIndex())
}
private fun instantiateAction(): Action? {
return getActionClass()?.let {
it.newInstance() as Action
}
}
private fun getActionClass(): Class<*>? {
val radioButtonID = automation_radioGroup.checkedRadioButtonId
val radioButton = automation_radioGroup.findViewById<RadioButton>(radioButtonID)
return radioButton?.let {
it.tag as Class<*>
}
}
private fun determineCheckedIndex(): Int {
for (i in 0 until automation_radioGroup.childCount) {
if ((automation_radioGroup.getChildAt(i) as RadioButton).isChecked)
return i
}
return -1
}
}

View file

@ -0,0 +1,89 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RadioButton
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import kotlinx.android.synthetic.main.automation_dialog_choose_trigger.*
import kotlinx.android.synthetic.main.okcancel.*
class ChooseTriggerDialog : DialogFragment() {
private var checkedIndex = -1
private var clickListener: OnClickListener? = null
interface OnClickListener {
fun onClick(newTriggerObject: Trigger?)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// restore checked radio button
savedInstanceState?.let { bundle ->
checkedIndex = bundle.getInt("checkedIndex")
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_choose_trigger, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
for (t in AutomationPlugin.getTriggerDummyObjects()) {
val radioButton = RadioButton(context)
radioButton.setText(t.friendlyName())
radioButton.tag = t.javaClass
automation_chooseTriggerRadioGroup.addView(radioButton)
}
if (checkedIndex != -1)
(automation_chooseTriggerRadioGroup.getChildAt(checkedIndex) as RadioButton).isChecked = true
// OK button
ok.setOnClickListener {
dismiss()
clickListener?.onClick(instantiateTrigger())
}
// Cancel button
cancel.setOnClickListener { dismiss() }
}
fun setOnClickListener(clickListener: OnClickListener) {
this.clickListener = clickListener
}
override fun onSaveInstanceState(bundle: Bundle) {
bundle.putInt("checkedIndex", determineCheckedIndex())
}
private fun instantiateTrigger(): Trigger? {
return getTriggerClass()?.let {
it.newInstance() as Trigger
}
}
private fun getTriggerClass(): Class<*>? {
val radioButtonID = automation_chooseTriggerRadioGroup.checkedRadioButtonId
val radioButton = automation_chooseTriggerRadioGroup.findViewById<RadioButton>(radioButtonID)
return radioButton?.let {
it.tag as Class<*>
}
}
private fun determineCheckedIndex(): Int {
for (i in 0 until automation_chooseTriggerRadioGroup.childCount) {
if ((automation_chooseTriggerRadioGroup.getChildAt(i) as RadioButton).isChecked)
return i
}
return -1
}
}

View file

@ -0,0 +1,62 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.actions.Action
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateAction
import kotlinx.android.synthetic.main.automation_dialog_action.*
import kotlinx.android.synthetic.main.okcancel.*
import org.json.JSONObject
class EditActionDialog : DialogFragment() {
private var action: Action? = null
private var actionPosition: Int = -1
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
actionPosition = bundle.getInt("actionPosition", -1)
bundle.getString("action")?.let { action = Action.instantiate(JSONObject(it)) }
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_action, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
action?.let {
automation_actionTitle.setText(it.friendlyName())
automation_editActionLayout.removeAllViews()
it.generateDialog(automation_editActionLayout)
}
// OK button
ok.setOnClickListener {
dismiss()
action?.let {
RxBus.send(EventAutomationUpdateAction(it, actionPosition))
}
}
// Cancel button
cancel.setOnClickListener { dismiss() }
}
override fun onSaveInstanceState(bundle: Bundle) {
super.onSaveInstanceState(bundle)
action?.let {
bundle.putInt("actionPosition", actionPosition)
bundle.putString("action", it.toJSON())
}
}
}

View file

@ -0,0 +1,154 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.AutomationEvent
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.automation.events.*
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.utils.ToastUtils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.automation_dialog_event.*
import kotlinx.android.synthetic.main.okcancel.*
class EditEventDialog : DialogFragment() {
private var actionListAdapter: ActionListAdapter? = null
private var event: AutomationEvent = AutomationEvent()
private var position: Int = -1
private var disposable: CompositeDisposable = CompositeDisposable()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
position = bundle.getInt("position", -1)
bundle.getString("event")?.let { event = AutomationEvent().fromJSON(it) }
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_event, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
automation_inputEventTitle.setText(event.title)
automation_triggerDescription.text = event.trigger.friendlyDescription()
automation_editTrigger.setOnClickListener {
val args = Bundle()
args.putString("trigger", event.trigger.toJSON())
val dialog = EditTriggerDialog()
dialog.arguments = args
fragmentManager?.let { dialog.show(it, "EditTriggerDialog") }
}
// setup action list view
fragmentManager?.let { actionListAdapter = ActionListAdapter(it, event.actions) }
automation_actionListView.layoutManager = LinearLayoutManager(context)
automation_actionListView.adapter = actionListAdapter
automation_addAction.setOnClickListener { fragmentManager?.let { ChooseActionDialog().show(it, "ChooseActionDialog") } }
// OK button
ok.setOnClickListener {
// check for title
val title = automation_inputEventTitle.text.toString()
if (title.isEmpty()) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_task_name)
return@setOnClickListener
}
event.title = title
// check for at least one trigger
val con = event.trigger as TriggerConnector
if (con.size() == 0) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_trigger)
return@setOnClickListener
}
// check for at least one action
if (event.actions.isEmpty()) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_action)
return@setOnClickListener
}
// store
if (position == -1)
AutomationPlugin.automationEvents.add(event)
else
AutomationPlugin.automationEvents[position] = event
dismiss()
RxBus.send(EventAutomationDataChanged())
}
// Cancel button
cancel.setOnClickListener { dismiss() }
showPreconditions()
disposable.add(RxBus
.toObservable(EventAutomationUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
actionListAdapter?.notifyDataSetChanged()
showPreconditions()
}, {})
)
disposable.add(RxBus
.toObservable(EventAutomationAddAction::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.addAction(it.action)
actionListAdapter?.notifyDataSetChanged()
}, {})
)
disposable.add(RxBus
.toObservable(EventAutomationUpdateTrigger::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.trigger = it.trigger
automation_triggerDescription.text = event.trigger.friendlyDescription()
}, {})
)
disposable.add(RxBus
.toObservable(EventAutomationUpdateAction::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.actions[it.position] = it.action
actionListAdapter?.notifyDataSetChanged()
}, {})
)
}
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
}
override fun onSaveInstanceState(bundle: Bundle) {
super.onSaveInstanceState(bundle)
bundle.putString("event", event.toJSON())
bundle.putInt("position", position)
}
private fun showPreconditions() {
val forcedTriggers = event.preconditions
if (forcedTriggers.size() > 0) {
automation_forcedTriggerDescription.visibility = View.VISIBLE
automation_forcedTriggerDescriptionLabel.visibility = View.VISIBLE
automation_forcedTriggerDescription.text = forcedTriggers.friendlyDescription()
} else {
automation_forcedTriggerDescription.visibility = View.GONE
automation_forcedTriggerDescriptionLabel.visibility = View.GONE
}
}
}

View file

@ -0,0 +1,50 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateTrigger
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import kotlinx.android.synthetic.main.automation_dialog_edit_trigger.*
import kotlinx.android.synthetic.main.okcancel.*
class EditTriggerDialog : DialogFragment() {
private var trigger: Trigger? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
bundle.getString("trigger")?.let { trigger = Trigger.instantiate(it) }
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_edit_trigger, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// display root trigger
trigger?.let { it.generateDialog(automation_layoutTrigger, fragmentManager) }
// OK button
ok.setOnClickListener {
dismiss()
RxBus.send(EventAutomationUpdateTrigger(trigger!!))
}
// Cancel button
cancel.setOnClickListener { dismiss() }
}
override fun onSaveInstanceState(bundle: Bundle) {
super.onSaveInstanceState(bundle)
trigger?.let { bundle.putString("trigger", it.toJSON()) }
}
}

View file

@ -0,0 +1,186 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.fragment.app.FragmentManager;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
public class TriggerListAdapter {
private final LinearLayout mRootLayout;
private final FragmentManager mFragmentManager;
private final Context mContext;
private final TriggerConnector mRootConnector;
public TriggerListAdapter(FragmentManager fragmentManager, Context context, LinearLayout rootLayout, TriggerConnector rootTrigger) {
mRootLayout = rootLayout;
mFragmentManager = fragmentManager;
mContext = context;
mRootConnector = rootTrigger;
build(fragmentManager);
}
public Context getContext() {
return mContext;
}
private FragmentManager getFM() {
return mFragmentManager;
}
private void destroy() {
mRootLayout.removeAllViews();
}
private void build(FragmentManager fragmentManager) {
for (int i = 0; i < mRootConnector.size(); ++i) {
final Trigger trigger = mRootConnector.get(i);
// spinner
if (i > 0) {
createSpinner(trigger);
}
// trigger layout
trigger.generateDialog(mRootLayout, fragmentManager);
// buttons
createButtons(fragmentManager, trigger);
}
if (mRootConnector.size() == 0) {
Button buttonAdd = new Button(mContext);
buttonAdd.setText(MainApp.gs(R.string.addnew));
buttonAdd.setOnClickListener(v -> {
ChooseTriggerDialog dialog = new ChooseTriggerDialog();
dialog.setOnClickListener(newTriggerObject -> {
mRootConnector.add(newTriggerObject);
rebuild(fragmentManager);
});
dialog.show(fragmentManager, "ChooseTriggerDialog");
});
mRootLayout.addView(buttonAdd);
}
}
private Spinner createSpinner() {
Spinner spinner = new Spinner(mContext);
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(mContext, android.R.layout.simple_spinner_item, TriggerConnector.Type.labels());
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerArrayAdapter);
return spinner;
}
private void createSpinner(Trigger trigger) {
final TriggerConnector connector = trigger.getConnector();
final int initialPosition = connector.getConnectorType().ordinal();
Spinner spinner = createSpinner();
spinner.setSelection(initialPosition);
spinner.setBackgroundColor(MainApp.gc(R.color.black_overlay));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(0, MainApp.dpToPx(8), 0, MainApp.dpToPx(8));
spinner.setLayoutParams(params);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (position != initialPosition) {
// connector type changed
changeConnector(getFM(), trigger, connector, TriggerConnector.Type.values()[position]);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
mRootLayout.addView(spinner);
}
private void createButtons(FragmentManager fragmentManager, Trigger trigger) {
// do not create buttons for TriggerConnector
if (trigger instanceof TriggerConnector) {
return;
}
// Button Layout
LinearLayout buttonLayout = new LinearLayout(mContext);
buttonLayout.setOrientation(LinearLayout.HORIZONTAL);
buttonLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
mRootLayout.addView(buttonLayout);
// Button [-]
Button buttonRemove = new Button(mContext);
buttonRemove.setText(MainApp.gs(R.string.delete_short));
buttonRemove.setOnClickListener(v -> {
final TriggerConnector connector = trigger.getConnector();
connector.remove(trigger);
connector.simplify().rebuildView(getFM());
});
buttonLayout.addView(buttonRemove);
// Button [+]
Button buttonAdd = new Button(mContext);
buttonAdd.setText(MainApp.gs(R.string.add_short));
buttonAdd.setOnClickListener(v -> {
ChooseTriggerDialog dialog = new ChooseTriggerDialog();
dialog.show(fragmentManager, "ChooseTriggerDialog");
dialog.setOnClickListener(newTriggerObject -> {
TriggerConnector connector = trigger.getConnector();
connector.add(connector.pos(trigger) + 1, newTriggerObject);
connector.simplify().rebuildView(getFM());
});
});
buttonLayout.addView(buttonAdd);
// Button [*]
Button buttonCopy = new Button(mContext);
buttonCopy.setText(MainApp.gs(R.string.copy_short));
buttonCopy.setOnClickListener(v -> {
TriggerConnector connector = trigger.getConnector();
connector.add(connector.pos(trigger) + 1, trigger.duplicate());
connector.simplify().rebuildView(getFM());
});
buttonLayout.addView(buttonCopy);
}
public void rebuild(FragmentManager fragmentManager) {
destroy();
build(fragmentManager);
}
public static void changeConnector(final FragmentManager fragmentManager, final Trigger trigger, final TriggerConnector connector, final TriggerConnector.Type newConnectorType) {
if (connector.size() > 2) {
// split connector
int pos = connector.pos(trigger) - 1;
TriggerConnector newConnector = new TriggerConnector(newConnectorType);
// move trigger from pos and pos+1 into new connector
for (int i = 0; i < 2; ++i) {
Trigger t = connector.get(pos);
newConnector.add(t);
connector.remove(t);
}
connector.add(pos, newConnector);
} else {
connector.changeConnectorType(newConnectorType);
}
connector.simplify().rebuildView(fragmentManager);
}
}

View file

@ -0,0 +1,123 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.annotation.StringRes;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class Comparator extends Element {
public enum Compare {
IS_LESSER,
IS_EQUAL_OR_LESSER,
IS_EQUAL,
IS_EQUAL_OR_GREATER,
IS_GREATER,
IS_NOT_AVAILABLE;
public @StringRes
int getStringRes() {
switch (this) {
case IS_LESSER:
return R.string.islesser;
case IS_EQUAL_OR_LESSER:
return R.string.isequalorlesser;
case IS_EQUAL:
return R.string.isequal;
case IS_EQUAL_OR_GREATER:
return R.string.isequalorgreater;
case IS_GREATER:
return R.string.isgreater;
case IS_NOT_AVAILABLE:
return R.string.isnotavailable;
default:
return R.string.unknown;
}
}
public <T extends Comparable> boolean check(T obj1, T obj2) {
if (obj1 == null || obj2 == null)
return this.equals(IS_NOT_AVAILABLE);
int comparison = obj1.compareTo(obj2);
switch (this) {
case IS_LESSER:
return comparison < 0;
case IS_EQUAL_OR_LESSER:
return comparison <= 0;
case IS_EQUAL:
return comparison == 0;
case IS_EQUAL_OR_GREATER:
return comparison >= 0;
case IS_GREATER:
return comparison > 0;
default:
return false;
}
}
public static List<String> labels() {
List<String> list = new ArrayList<>();
for (Compare c : Compare.values()) {
list.add(MainApp.gs(c.getStringRes()));
}
return list;
}
}
private Compare compare = Compare.IS_EQUAL;
public Comparator() {
super();
}
public Comparator(Comparator another) {
super();
compare = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
Spinner spinner = new Spinner(root.getContext());
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, Compare.labels());
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerArrayAdapter);
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
spinnerParams.setMargins(0, MainApp.dpToPx(4), 0, MainApp.dpToPx(4));
spinner.setLayoutParams(spinnerParams);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
compare = Compare.values()[position];
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
spinner.setSelection(compare.ordinal());
root.addView(spinner);
}
public Compare getValue() {
return compare;
}
public Comparator setValue(Compare compare) {
this.compare = compare;
return this;
}
}

View file

@ -0,0 +1,90 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.annotation.StringRes;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class ComparatorExists extends Element {
public enum Compare {
EXISTS,
NOT_EXISTS;
public @StringRes
int getStringRes() {
switch (this) {
case EXISTS:
return R.string.exists;
case NOT_EXISTS:
return R.string.notexists;
default:
return R.string.unknown;
}
}
public static List<String> labels() {
List<String> list = new ArrayList<>();
for (Compare c : Compare.values()) {
list.add(MainApp.gs(c.getStringRes()));
}
return list;
}
}
private Compare compare = Compare.EXISTS;
public ComparatorExists() {
super();
}
public ComparatorExists(ComparatorExists another) {
super();
compare = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
Spinner spinner = new Spinner(root.getContext());
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, Compare.labels());
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerArrayAdapter);
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
spinnerParams.setMargins(0, MainApp.dpToPx(4), 0, MainApp.dpToPx(4));
spinner.setLayoutParams(spinnerParams);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
compare = Compare.values()[position];
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
spinner.setSelection(compare.ordinal());
root.addView(spinner);
}
public Compare getValue() {
return compare;
}
public ComparatorExists setValue(Compare compare) {
this.compare = compare;
return this;
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.widget.LinearLayout;
public abstract class Element {
public abstract void addToLayout(LinearLayout root);
}

View file

@ -0,0 +1,99 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.LinearLayout;
import java.text.DecimalFormat;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.utils.NumberPicker;
public class InputBg extends Element {
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if (units.equals(Constants.MMOL)) {
value = Math.max(value, 4d);
value = Math.min(value, 15d);
} else {
value = Math.max(value, 72d);
value = Math.min(value, 270d);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
};
private String units = Constants.MGDL;
private double value;
double minValue;
private double maxValue;
private double step;
private DecimalFormat decimalFormat;
public InputBg() {
super();
setUnits(ProfileFunctions.getInstance().getProfileUnits());
}
public InputBg(InputBg another) {
super();
value = another.getValue();
setUnits(another.getUnits());
}
@Override
public void addToLayout(LinearLayout root) {
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
numberPicker.setOnValueChangedListener(value -> this.value = value);
root.addView(numberPicker);
}
public String getUnits() {
return units;
}
public InputBg setUnits(String units) {
// set default initial value
if (units.equals(Constants.MMOL)) {
// mmol
minValue = 2;
maxValue = 30;
step = 0.1;
decimalFormat = new DecimalFormat("0.0");
} else {
// mg/dL
minValue = 40;
maxValue = 540;
step = 1;
decimalFormat = new DecimalFormat("0");
}
// make sure that value is in range
textWatcher.afterTextChanged(null);
this.units = units;
return this;
}
public InputBg setValue(double value) {
this.value = value;
return this;
}
public double getValue() {
return value;
}
}

View file

@ -0,0 +1,28 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.widget.Button;
import android.widget.LinearLayout;
public class InputButton extends Element {
String text;
Runnable runnable;
public InputButton(String text, Runnable runnable) {
this.text = text;
this.runnable = runnable;
}
@Override
public void addToLayout(LinearLayout root) {
Button button = new Button(root.getContext());
button.setText(text);
button.setOnClickListener(view -> {
if (runnable != null)
runnable.run();
});
root.addView(button);
}
}

View file

@ -0,0 +1,151 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.annotation.StringRes;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.NumberPicker;
public class InputDelta extends Element {
private Comparator.Compare compare = Comparator.Compare.IS_EQUAL;
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
};
public enum DeltaType {
DELTA,
SHORT_AVERAGE,
LONG_AVERAGE;
public @StringRes
int getStringRes() {
switch (this) {
case DELTA:
return R.string.delta;
case SHORT_AVERAGE:
return R.string.short_avgdelta;
case LONG_AVERAGE:
return R.string.long_avgdelta;
default:
return R.string.unknown;
}
}
public static List<String> labels() {
List<String> list = new ArrayList<>();
for (DeltaType d : DeltaType.values()) {
list.add(MainApp.gs(d.getStringRes()));
}
return list;
}
}
private double value;
double minValue;
double maxValue;
private double step;
private DecimalFormat decimalFormat;
private DeltaType deltaType;
NumberPicker numberPicker;
public InputDelta() {
super();
}
public InputDelta(double value, double minValue, double maxValue, double step, DecimalFormat decimalFormat, DeltaType deltaType) {
super();
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.step = step;
this.decimalFormat = decimalFormat;
this.deltaType = deltaType;
}
public InputDelta(InputDelta another) {
super();
value = another.getValue();
minValue = another.minValue;
maxValue = another.maxValue;
step = another.step;
decimalFormat = another.decimalFormat;
deltaType = another.deltaType;
}
@Override
public void addToLayout(LinearLayout root) {
Spinner spinner = new Spinner(root.getContext());
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, DeltaType.labels());
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerArrayAdapter);
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
spinnerParams.setMargins(0, MainApp.dpToPx(4), 0, MainApp.dpToPx(4));
spinner.setLayoutParams(spinnerParams);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
deltaType = DeltaType.values()[position];
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
spinner.setSelection(this.deltaType.ordinal());
// root.addView(spinner);
numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
numberPicker.setOnValueChangedListener(value -> this.value = value);
LinearLayout l = new LinearLayout(root.getContext());
l.setOrientation(LinearLayout.VERTICAL);
l.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
l.addView(spinner);
l.addView(numberPicker);
root.addView(l);
}
public InputDelta setValue(double value, DeltaType type) {
this.value = value;
this.deltaType = type;
if (numberPicker != null)
numberPicker.setValue(value);
return this;
}
public double getValue() {
return value;
}
public DeltaType getDeltaType() {
return deltaType;
}
}

View file

@ -0,0 +1,77 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.LinearLayout;
import java.text.DecimalFormat;
import info.nightscout.androidaps.utils.NumberPicker;
public class InputDouble extends Element {
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
};
private double value;
double minValue;
double maxValue;
private double step;
private DecimalFormat decimalFormat;
NumberPicker numberPicker;
public InputDouble() {
super();
}
public InputDouble(double value, double minValue, double maxValue, double step, DecimalFormat decimalFormat) {
super();
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.step = step;
this.decimalFormat = decimalFormat;
}
public InputDouble(InputDouble another) {
super();
value = another.getValue();
minValue = another.minValue;
maxValue = another.maxValue;
step = another.step;
decimalFormat = another.decimalFormat;
}
@Override
public void addToLayout(LinearLayout root) {
numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
numberPicker.setOnValueChangedListener(value -> this.value = value);
root.addView(numberPicker);
}
public InputDouble setValue(double value) {
this.value = value;
if (numberPicker != null)
numberPicker.setValue(value);
return this;
}
public double getValue() {
return value;
}
}

View file

@ -0,0 +1,61 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.widget.LinearLayout;
import java.text.DecimalFormat;
import info.nightscout.androidaps.utils.NumberPicker;
public class InputDuration extends Element {
public enum TimeUnit {
MINUTES,
HOURS
}
private TimeUnit unit;
private int value;
public InputDuration(int value, TimeUnit unit) {
this.unit = unit;
this.value = value;
}
@Override
public void addToLayout(LinearLayout root) {
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
if (unit.equals(TimeUnit.MINUTES)) {
// Minutes
numberPicker.setParams(0d, 0d, 24 * 60d, 10d, new DecimalFormat("0"), false, null);
} else {
// Hours
numberPicker.setParams(0d, 0d, 24d, 1d, new DecimalFormat("0"), false, null);
}
numberPicker.setValue((double) value);
numberPicker.setOnValueChangedListener(value -> this.value = (int) value);
root.addView(numberPicker);
}
TimeUnit getUnit() {
return unit;
}
public double getValue() {
return value;
}
public void setMinutes(int value) {
if (unit.equals(TimeUnit.MINUTES)) {
this.value = value;
} else {
this.value = value / 60;
}
}
public int getMinutes() {
if (unit.equals(TimeUnit.MINUTES)) {
return value;
} else {
return value * 60;
}
}
}

View file

@ -0,0 +1,57 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.LinearLayout;
import java.text.DecimalFormat;
import info.nightscout.androidaps.utils.NumberPicker;
public class InputInsulin extends Element {
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
value = Math.max(value, -20d);
value = Math.min(value, 20d);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
};
private double value;
public InputInsulin() {
super();
}
public InputInsulin(InputInsulin another) {
super();
value = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(0d, -20d, 20d, 0.1, new DecimalFormat("0.0"), true, null, textWatcher);
numberPicker.setValue(value);
numberPicker.setOnValueChangedListener(value -> this.value = value);
root.addView(numberPicker);
}
public double getValue() {
return value;
}
public InputInsulin setValue(double value) {
this.value = value;
return this;
}
}

View file

@ -0,0 +1,58 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.LinearLayout;
import java.text.DecimalFormat;
import info.nightscout.androidaps.utils.NumberPicker;
public class InputPercent extends Element {
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
value = Math.max(value, 70d);
value = Math.min(value, 130d);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
};
private double value;
public InputPercent() {
super();
value = 100d;
}
public InputPercent(InputPercent another) {
super();
value = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(100d, 70d, 130d, 5d, new DecimalFormat("0"), true, null, textWatcher);
numberPicker.setValue(value);
numberPicker.setOnValueChangedListener(value -> this.value = value);
root.addView(numberPicker);
}
public double getValue() {
return value;
}
public InputPercent setValue(double value) {
this.value = value;
return this;
}
}

View file

@ -0,0 +1,56 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
public class InputString extends Element {
TextWatcher textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
value = s.toString();
}
};
private String value = "";
public InputString() {
super();
}
public InputString(InputString another) {
super();
value = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
EditText editText = new EditText(root.getContext());
editText.setText(value);
editText.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
editText.addTextChangedListener(textWatcher);
root.addView(editText);
}
public InputString setValue(String value) {
this.value = value;
return this;
}
public String getValue() {
return value;
}
}

View file

@ -0,0 +1,59 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.graphics.Typeface;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import info.nightscout.androidaps.MainApp;
public class LabelWithElement extends Element {
final Element element;
final String textPre;
final String textPost;
public LabelWithElement(String textPre, String textPost, Element element) {
this.element = element;
this.textPre = textPre;
this.textPost = textPost;
}
@Override
public void addToLayout(LinearLayout root) {
// container layout
LinearLayout layout = new LinearLayout(root.getContext());
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
// text view pre element
int px = MainApp.dpToPx(10);
TextView textViewPre = new TextView(root.getContext());
textViewPre.setText(textPre);
textViewPre.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
//textViewPre.setWidth(MainApp.dpToPx(120));
textViewPre.setPadding(px, px, px, px);
textViewPre.setTypeface(textViewPre.getTypeface(), Typeface.BOLD);
layout.addView(textViewPre);
// add element to layout
element.addToLayout(layout);
// text view post element
if (textPost != null) {
px = MainApp.dpToPx(5);
TextView textViewPost = new TextView(root.getContext());
textViewPost.setText(textPost);
textViewPost.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
//textViewPost.setWidth(MainApp.dpToPx(45));
textViewPost.setPadding(px, px, px, px);
textViewPost.setTypeface(textViewPost.getTypeface(), Typeface.BOLD);
layout.addView(textViewPost);
}
// add layout to root layout
root.addView(layout);
}
}

View file

@ -0,0 +1,26 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.widget.LinearLayout;
import java.util.ArrayList;
public class LayoutBuilder {
ArrayList<Element> mElements = new ArrayList<>();
public LayoutBuilder add(Element element) {
mElements.add(element);
return this;
}
public LayoutBuilder add(Element element, boolean condition) {
if (condition) mElements.add(element);
return this;
}
public void build(LinearLayout layout) {
for (Element e : mElements) {
e.addToLayout(layout);
}
}
}

View file

@ -0,0 +1,36 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.graphics.Typeface;
import android.widget.LinearLayout;
import android.widget.TextView;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class StaticLabel extends Element {
String label;
public StaticLabel(String label) {
super();
this.label = label;
}
public StaticLabel(int resourceId) {
super();
this.label = MainApp.gs(resourceId);
}
@Override
public void addToLayout(LinearLayout root) {
// text view pre element
int px = MainApp.dpToPx(10);
TextView textView = new TextView(root.getContext());
textView.setText(label);
// textViewPre.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
textView.setPadding(px, px, px, px);
textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
textView.setBackgroundColor(MainApp.gc(R.color.mdtp_line_dark));
// add element to layout
root.addView(textView);
}
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.general.automation.actions.Action
class EventAutomationAddAction(val action: Action) : Event()

View file

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

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.general.automation.actions.Action
class EventAutomationUpdateAction(val action: Action, val position : Int) : Event() {
}

View file

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

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
class EventAutomationUpdateTrigger(val trigger: Trigger) : Event()

View file

@ -0,0 +1,94 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import javax.annotation.Nullable;
public abstract class Trigger {
TriggerConnector connector = null;
long lastRun;
Trigger() {
}
public TriggerConnector getConnector() {
return connector;
}
public abstract boolean shouldRun();
public abstract String toJSON();
/*package*/
abstract Trigger fromJSON(String data);
public abstract int friendlyName();
public abstract String friendlyDescription();
public abstract Optional<Integer> icon();
public void executed(long time) {
lastRun = time;
}
public long getLastRun() {
return lastRun;
}
public abstract Trigger duplicate();
public static Trigger instantiate(String json) {
try {
return instantiate(new JSONObject(json));
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
@Nullable
public static Trigger instantiate(JSONObject object) {
try {
String type = object.getString("type");
JSONObject data = object.getJSONObject("data");
Class clazz = Class.forName(type);
return ((Trigger) clazz.newInstance()).fromJSON(data.toString());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
e.printStackTrace();
}
return null;
}
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
TextView title = new TextView(root.getContext());
title.setText(friendlyName());
root.addView(title);
}
@Nullable
Activity scanForActivity(Context cont) {
if (cont == null)
return null;
else if (cont instanceof Activity)
return (Activity) cont;
else if (cont instanceof ContextWrapper)
return scanForActivity(((ContextWrapper) cont).getBaseContext());
return null;
}
}

View file

@ -0,0 +1,153 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.T;
public class TriggerAutosensValue extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private final int minValue = (int) (SP.getDouble("openapsama_autosens_min", 0.7d) * 100);
private final int maxValue = (int) (SP.getDouble("openapsama_autosens_max", 1.2d) * 100);
private final double step = 1;
private DecimalFormat decimalFormat = new DecimalFormat("1");
private InputDouble value = new InputDouble(100, (double) minValue, (double) maxValue, step, decimalFormat);
private Comparator comparator = new Comparator();
public TriggerAutosensValue() {
super();
}
private TriggerAutosensValue(TriggerAutosensValue triggerAutosensValue) {
super();
value = new InputDouble(triggerAutosensValue.value);
lastRun = triggerAutosensValue.lastRun;
comparator = new Comparator(triggerAutosensValue.comparator);
}
public double getValue() {
return value.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getLastAutosensData("Automation trigger");
if (autosensData == null)
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
return true;
else
return false;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
boolean doRun = comparator.getValue().check((autosensData.autosensResult.ratio), getValue() / 100d);
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerAutosensValue.class.getName());
JSONObject data = new JSONObject();
data.put("value", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
value.setValue(JsonHelper.safeGetDouble(d, "value"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.autosenslabel;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.autosenscompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.as);
}
@Override
public Trigger duplicate() {
return new TriggerAutosensValue(this);
}
TriggerAutosensValue setValue(int requestedValue) {
this.value.setValue(requestedValue);
return this;
}
TriggerAutosensValue lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerAutosensValue comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.autosenslabel))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.autosenslabel) + ": ", "", value))
.build(root);
}
}

View file

@ -0,0 +1,169 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputBg;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerBg extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private InputBg bg = new InputBg();
private Comparator comparator = new Comparator();
public TriggerBg() {
super();
}
private TriggerBg(TriggerBg triggerBg) {
super();
bg = new InputBg(triggerBg.bg);
comparator = new Comparator(triggerBg.comparator);
lastRun = triggerBg.lastRun;
}
public double getValue() {
return bg.getValue();
}
public Comparator getComparator() {
return comparator;
}
public String getUnits() {
return bg.getUnits();
}
public long getLastRun() {
return lastRun;
}
@Override
public synchronized boolean shouldRun() {
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
if (glucoseStatus == null && comparator.getValue().equals(Comparator.Compare.IS_NOT_AVAILABLE)) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
if (glucoseStatus == null)
return false;
boolean doRun = comparator.getValue().check(glucoseStatus.glucose, Profile.toMgdl(bg.getValue(), bg.getUnits()));
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerBg.class.getName());
JSONObject data = new JSONObject();
data.put("bg", bg.getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
data.put("units", bg.getUnits());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
bg.setUnits(JsonHelper.safeGetString(d, "units"));
bg.setValue(JsonHelper.safeGetDouble(d, "bg"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.glucose;
}
@Override
public String friendlyDescription() {
if (comparator.getValue().equals(Comparator.Compare.IS_NOT_AVAILABLE))
return MainApp.gs(R.string.glucoseisnotavailable);
else {
return MainApp.gs(bg.getUnits().equals(Constants.MGDL) ? R.string.glucosecomparedmgdl : R.string.glucosecomparedmmol, MainApp.gs(comparator.getValue().getStringRes()), bg.getValue(), bg.getUnits());
}
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_cp_bgcheck);
}
@Override
public Trigger duplicate() {
return new TriggerBg(this);
}
TriggerBg setValue(double value) {
bg.setValue(value);
return this;
}
TriggerBg lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerBg comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
TriggerBg setUnits(String units) {
bg.setUnits(units);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.glucose))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.glucose_u, bg.getUnits()), "", bg))
.build(root);
}
}

View file

@ -0,0 +1,154 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerBolusAgo extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private final double step = 1;
private DecimalFormat decimalFormat = new DecimalFormat("1");
public InputDuration minutesAgo = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
private Comparator comparator = new Comparator();
public TriggerBolusAgo() {
super();
}
private TriggerBolusAgo(TriggerBolusAgo triggerBolusAgo) {
super();
minutesAgo = new InputDuration(triggerBolusAgo.minutesAgo.getMinutes(), InputDuration.TimeUnit.MINUTES);
lastRun = triggerBolusAgo.lastRun;
comparator = new Comparator(triggerBolusAgo.comparator);
}
public double getValue() {
return minutesAgo.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
long lastBolusTime = TreatmentsPlugin.getPlugin().getLastBolusTime(false);
if (lastBolusTime == 0)
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
return true;
else
return false;
double minutesAgo = (double) (DateUtil.now() - lastBolusTime) / (60 * 1000);
if (L.isEnabled(L.AUTOMATION))
log.debug("LastBolus min ago: " + minutesAgo);
boolean doRun = comparator.getValue().check((minutesAgo), getValue());
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerBolusAgo.class.getName());
JSONObject data = new JSONObject();
data.put("minutesAgo", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
minutesAgo.setMinutes(JsonHelper.safeGetInt(d, "minutesAgo"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.lastboluslabel;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.lastboluscompared, MainApp.gs(comparator.getValue().getStringRes()), (int) getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_bolus);
}
@Override
public Trigger duplicate() {
return new TriggerBolusAgo(this);
}
TriggerBolusAgo setValue(int requestedValue) {
this.minutesAgo.setMinutes(requestedValue);
return this;
}
TriggerBolusAgo lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerBolusAgo comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.lastboluslabel))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.lastboluslabel) + ": ", "", minutesAgo))
.build(root);
}
}

View file

@ -0,0 +1,153 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.T;
public class TriggerCOB extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private final int minValue = 0;
private final int maxValue = (int) (SP.getInt(R.string.key_treatmentssafety_maxcarbs, 48));
private final double step = 1;
private DecimalFormat decimalFormat = new DecimalFormat("1");
private InputDouble value = new InputDouble(0, (double) minValue, (double) maxValue, step, decimalFormat);
private Comparator comparator = new Comparator();
public TriggerCOB() {
super();
}
private TriggerCOB(TriggerCOB triggerCOB) {
super();
value = new InputDouble(triggerCOB.value);
lastRun = triggerCOB.lastRun;
comparator = new Comparator(triggerCOB.comparator);
}
public double getValue() {
return value.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
CobInfo cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "AutomationTriggerCOB");
if (cobInfo == null)
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
return true;
else
return false;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
boolean doRun = comparator.getValue().check((cobInfo.displayCob), getValue());
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerCOB.class.getName());
JSONObject data = new JSONObject();
data.put("carbs", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
value.setValue(JsonHelper.safeGetDouble(d, "carbs"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.triggercoblabel;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.cobcompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_cp_bolus_carbs);
}
@Override
public Trigger duplicate() {
return new TriggerCOB(this);
}
TriggerCOB setValue(int requestedValue) {
this.value.setValue(requestedValue);
return this;
}
TriggerCOB lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerCOB comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.triggercoblabel))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.triggercoblabel) + ": ", "", value))
.build(root);
}
}

View file

@ -0,0 +1,277 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.StringRes;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.dialogs.TriggerListAdapter;
import info.nightscout.androidaps.utils.JsonHelper;
public class TriggerConnector extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
public enum Type {
AND,
OR,
XOR;
public boolean apply(boolean a, boolean b) {
switch (this) {
case AND:
return a && b;
case OR:
return a || b;
case XOR:
return a ^ b;
}
return false;
}
public @StringRes
int getStringRes() {
switch (this) {
case OR:
return R.string.or;
case XOR:
return R.string.xor;
default:
case AND:
return R.string.and;
}
}
public static List<String> labels() {
List<String> list = new ArrayList<>();
for (Type t : values()) {
list.add(MainApp.gs(t.getStringRes()));
}
return list;
}
}
public static void fillIconSet(TriggerConnector connector, HashSet<Integer> set) {
for (Trigger t : connector.list) {
if (t instanceof TriggerConnector) {
fillIconSet((TriggerConnector) t, set);
} else {
Optional<Integer> icon = t.icon();
if (icon.isPresent()) {
set.add(icon.get());
}
}
}
}
protected List<Trigger> list = new ArrayList<>();
private Type connectorType;
public TriggerConnector() {
connectorType = Type.AND;
}
public TriggerConnector(Type connectorType) {
this.connectorType = connectorType;
}
public void changeConnectorType(Type type) {
this.connectorType = type;
}
public Type getConnectorType() {
return connectorType;
}
public synchronized void add(Trigger t) {
list.add(t);
t.connector = this;
}
public synchronized void add(int pos, Trigger t) {
list.add(pos, t);
t.connector = this;
}
public synchronized boolean remove(Trigger t) {
return list.remove(t);
}
public int size() {
return list.size();
}
public Trigger get(int i) {
return list.get(i);
}
public int pos(Trigger trigger) {
for (int i = 0; i < list.size(); ++i) {
if (list.get(i) == trigger) return i;
}
return -1;
}
@Override
public synchronized boolean shouldRun() {
boolean result = true;
// check first trigger
if (list.size() > 0)
result = list.get(0).shouldRun();
// check all others
for (int i = 1; i < list.size(); ++i) {
result = connectorType.apply(result, list.get(i).shouldRun());
}
if (result)
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription().replace("\n", " "));
return result;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerConnector.class.getName());
JSONObject data = new JSONObject();
data.put("connectorType", connectorType.toString());
JSONArray array = new JSONArray();
for (Trigger t : list) {
array.put(t.toJSON());
}
data.put("triggerList", array);
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
connectorType = Type.valueOf(JsonHelper.safeGetString(d, "connectorType"));
JSONArray array = d.getJSONArray("triggerList");
list.clear();
for (int i = 0; i < array.length(); i++) {
Trigger newItem = instantiate(new JSONObject(array.getString(i)));
add(newItem);
}
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return connectorType.getStringRes();
}
@Override
public String friendlyDescription() {
int counter = 0;
StringBuilder result = new StringBuilder();
for (Trigger t : list) {
if (counter++ > 0) result.append("\n").append(MainApp.gs(friendlyName())).append("\n");
result.append(t.friendlyDescription());
}
return result.toString();
}
@Override
public Optional<Integer> icon() {
return Optional.absent();
}
@Override
public void executed(long time) {
for (int i = 0; i < list.size(); ++i) {
list.get(i).executed(time);
}
}
@Override
public Trigger duplicate() {
return null;
}
private TriggerListAdapter adapter;
public void rebuildView(FragmentManager fragmentManager) {
if (adapter != null)
adapter.rebuild(fragmentManager);
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
final int padding = MainApp.dpToPx(5);
root.setPadding(padding, padding, padding, padding);
root.setBackgroundResource(R.drawable.border_automation_unit);
LinearLayout triggerListLayout = new LinearLayout(root.getContext());
triggerListLayout.setOrientation(LinearLayout.VERTICAL);
triggerListLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
root.addView(triggerListLayout);
adapter = new TriggerListAdapter(fragmentManager, root.getContext(), triggerListLayout, this);
}
public TriggerConnector simplify() {
// simplify children
for (int i = 0; i < size(); ++i) {
if (get(i) instanceof TriggerConnector) {
TriggerConnector t = (TriggerConnector) get(i);
t.simplify();
}
}
// drop connector with only 1 element
if (size() == 1 && get(0) instanceof TriggerConnector) {
TriggerConnector c = (TriggerConnector) get(0);
remove(c);
changeConnectorType(c.getConnectorType());
for (Trigger t : c.list) {
add(t);
}
c.list.clear();
return simplify();
}
// merge connectors
if (connector != null && (connector.getConnectorType().equals(connectorType) || size() == 1)) {
final int pos = connector.pos(this);
connector.remove(this);
// move triggers of child connector into parent connector
for (int i = size() - 1; i >= 0; --i) {
connector.add(pos, get(i));
}
list.clear();
return connector.simplify();
}
return this;
}
}

View file

@ -0,0 +1,208 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
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.logging.L;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDelta;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDelta.DeltaType;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerDelta extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private double minValue = 0d;
private double maxValue = 1d;
private double step = 1;
private DecimalFormat decimalFormat = new DecimalFormat("1");
private String units;
private DeltaType deltaType;
private InputDelta value = new InputDelta( (double) minValue,(double) minValue, (double) maxValue, step, decimalFormat, deltaType);
private Comparator comparator = new Comparator();
public TriggerDelta() {
super();
this.units = ProfileFunctions.getInstance().getProfileUnits();
initializer();
}
private TriggerDelta(TriggerDelta triggerDelta) {
super();
this.units = ProfileFunctions.getInstance().getProfileUnits();
initializer();
value = triggerDelta.value;
lastRun = triggerDelta.lastRun;
}
public double getValue() {
deltaType = value.getDeltaType();
return value.getValue();
}
public DeltaType getType() {
return deltaType;
}
public String getUnits() {
return this.units;
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
if (glucoseStatus == null)
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
return true;
else
return false;
// Setting type of delta
double delta;
if (deltaType == DeltaType.SHORT_AVERAGE)
delta = glucoseStatus.short_avgdelta;
else if (deltaType == DeltaType.LONG_AVERAGE)
delta = glucoseStatus.long_avgdelta;
else
delta = glucoseStatus.delta;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
boolean doRun = comparator.getValue().check(delta, Profile.toMgdl(value.getValue(), this.units));
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: delta is " + delta + " " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerDelta.class.getName());
JSONObject data = new JSONObject();
data.put("value", getValue());
data.put("units", units);
data.put("lastRun", lastRun);
data.put("deltaType", getType());
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
units = JsonHelper.safeGetString(d, "units");
deltaType = DeltaType.valueOf(JsonHelper.safeGetString(d, "deltaType", ""));
value.setValue(JsonHelper.safeGetDouble(d, "value"), deltaType);
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.deltalabel;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.deltacompared, MainApp.gs(comparator.getValue().getStringRes()), getValue(), MainApp.gs(deltaType.getStringRes()));
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_auto_delta);
}
@Override
public Trigger duplicate() {
return new TriggerDelta(this);
}
TriggerDelta setValue(double requestedValue, DeltaType requestedType) {
this.value.setValue(requestedValue, requestedType);
this.deltaType = requestedType;
return this;
}
TriggerDelta setUnits(String units) {
this.units = units;
return this;
}
void initializer(){
if (this.units.equals(Constants.MMOL)) {
this.maxValue = 4d;
this.minValue = -4d;
this.step = 0.1d;
this.decimalFormat = new DecimalFormat("0.1");
this.deltaType = DeltaType.DELTA;
} else {
this.maxValue = 72d;
this.minValue = -72d;
this.step = 1d;
this.deltaType = DeltaType.DELTA;
}
value = new InputDelta( (double) minValue,(double) minValue, (double) maxValue, step, decimalFormat, deltaType);
}
TriggerDelta lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerDelta comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.deltalabel))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.deltalabel) + ": ", "", value))
.build(root);
}
}

View file

@ -0,0 +1,146 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputInsulin;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerIob extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private InputInsulin insulin = new InputInsulin();
private Comparator comparator = new Comparator();
public TriggerIob() {
super();
}
private TriggerIob(TriggerIob triggerIob) {
super();
insulin = new InputInsulin(triggerIob.insulin);
comparator = new Comparator(triggerIob.comparator);
lastRun = triggerIob.lastRun;
}
public double getValue() {
return insulin.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null)
return false;
IobTotal iob = IobCobCalculatorPlugin.getPlugin().calculateFromTreatmentsAndTempsSynchronized(DateUtil.now(), profile);
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
boolean doRun = comparator.getValue().check(iob.iob, getValue());
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerIob.class.getName());
JSONObject data = new JSONObject();
data.put("insulin", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
insulin.setValue(JsonHelper.safeGetDouble(d, "insulin"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.iob;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.iobcompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_keyboard_capslock);
}
@Override
public Trigger duplicate() {
return new TriggerIob(this);
}
TriggerIob setValue(double threshold) {
insulin.setValue(threshold);
return this;
}
TriggerIob lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerIob comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.iob))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.iob_u), "", insulin))
.build(root);
}
}

View file

@ -0,0 +1,167 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.location.Location;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.InputButton;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
import info.nightscout.androidaps.plugins.general.automation.elements.InputString;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.services.LocationService;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerLocation extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
InputDouble latitude = new InputDouble(0d, -90d, +90d, 0.000001d, new DecimalFormat("0.000000"));
InputDouble longitude = new InputDouble(0d, -180d, +180d, 0.000001d, new DecimalFormat("0.000000"));
InputDouble distance = new InputDouble(200d, 0, 100000, 10d, new DecimalFormat("0"));
InputString name = new InputString();
private Runnable buttonAction = () -> {
Location location = LocationService.getLastLocation();
if (location != null) {
latitude.setValue(location.getLatitude());
longitude.setValue(location.getLongitude());
log.debug(String.format("Grabbed location: %f %f", latitude.getValue(), longitude.getValue()));
}
};
public TriggerLocation() {
super();
}
private TriggerLocation(TriggerLocation triggerLocation) {
super();
latitude = new InputDouble(triggerLocation.latitude.getValue(), -90d, +90d, 0.00001d, new DecimalFormat("0.00000"));
longitude = new InputDouble(triggerLocation.longitude.getValue(), -180d, +180d, 0.00001d, new DecimalFormat("0.00000"));
distance = new InputDouble(200d, 0, 100000, 10d, new DecimalFormat("0"));
lastRun = triggerLocation.lastRun;
}
@Override
public synchronized boolean shouldRun() {
Location location = LocationService.getLastLocation();
if (location == null)
return false;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
Location a = new Location("Trigger");
a.setLatitude(latitude.getValue());
a.setLongitude(longitude.getValue());
double calculatedDistance = location.distanceTo(a);
if (calculatedDistance < distance.getValue()) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerLocation.class.getName());
JSONObject data = new JSONObject();
data.put("latitude", latitude.getValue());
data.put("longitude", longitude.getValue());
data.put("distance", distance.getValue());
data.put("name", name.getValue());
data.put("lastRun", lastRun);
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
latitude.setValue(JsonHelper.safeGetDouble(d, "latitude"));
longitude.setValue(JsonHelper.safeGetDouble(d, "longitude"));
distance.setValue(JsonHelper.safeGetDouble(d, "distance"));
name.setValue(JsonHelper.safeGetString(d, "name"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.location;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.locationis, name.getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_location_on);
}
@Override
public Trigger duplicate() {
return new TriggerLocation(this);
}
TriggerLocation setLatitude(double value) {
latitude.setValue(value);
return this;
}
TriggerLocation setLongitude(double value) {
longitude.setValue(value);
return this;
}
TriggerLocation setdistance(double value) {
distance.setValue(value);
return this;
}
TriggerLocation lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.location))
.add(new LabelWithElement(MainApp.gs(R.string.name_short), "", name))
.add(new LabelWithElement(MainApp.gs(R.string.latitude_short), "", latitude))
.add(new LabelWithElement(MainApp.gs(R.string.longitude_short), "", longitude))
.add(new LabelWithElement(MainApp.gs(R.string.distance_short), "", distance))
.add(new InputButton(MainApp.gs(R.string.currentlocation), buttonAction), LocationService.getLastLocation() != null)
.build(root);
}
}

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