diff --git a/.gitignore b/.gitignore index 98b73cb7cb..c7ed9df2f5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,17 @@ /captures *.apk build/ -.idea/ +.idea/* +!.idea/codeStyles/ app/src/main/jniLibs full/ debug/ release/ app/com.crashlytics.settings.json -app/session_analytics.tap \ No newline at end of file +app/session_analytics.tap +.project +.settings/org.eclipse.buildship.core.prefs +app/.classpath +app/.settings/org.eclipse.buildship.core.prefs +wear/.classpath +wear/.settings/org.eclipse.buildship.core.prefs diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..d1c3d63de5 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,134 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..79ee123c2b --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..d15d2440fd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +This document speciffy hints and good practices for source code contributions. + +AndroidAPS is community effort and all contributions are welcome! If you wish help us improving AndroidAPS - please read and try to adhere to +this guidelines, to make the development and process of change aproval as smooth as possible :) + +General rules +============= + +* There are plenty of ways you can help, some of them are listed on wiki: + https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/How-can-I-help.html +* If you wish to help with documentation or translating: + https://androidaps.readthedocs.io/en/latest/EN/translations.html + +Development guidelines +====================== + +Coding convetions +----------------- +1. Use Android Studio with default indents (4 chars, use spaces) +2. Use autoformat feature CTRL-ALT-L in every changed file before commit + +Commiting Changes / Pull Requests +--------------------------------- + +1. Make fork of repository on github +2. Create separate branch for each feature, branch from most recent dev +3. Commit all changes to your fork +4. When ready, rebase on top of dev and make pull request to main repo + +Naming Conventions for Pull Requests / Branches +----------------------------------------------- + +TODO + +Translations +------------ + +* If possible, always use Android translation mechanism (with strings.xml and @strings/id) instead of hardcoded texts +* Provide only English strings - all other languages will be crowd translated via Crowdn https://translations.androidaps.org/ + +Hints +----- + +* Start small, it is easier to review smaller changes that affect fewer parts of code +* Take a look into Issues list (https://github.com/MilosKozak/AndroidAPS/issues) - maybe there is somthing you can fix or implement +* For new features, make sure there is Issue to track progress and have on-topic discussion +* Reach out to community, discuss idea on Gitter (https://gitter.im/MilosKozak/AndroidAPS) +* Speak with other developers to minimise merge conflicts. Find out who worked, working or plan to work on speciffic issue or part of app diff --git a/app/build.gradle b/app/build.gradle index 94aaa78834..b7815678f1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -109,7 +109,7 @@ android { targetSdkVersion 28 multiDexEnabled true versionCode 1500 - version "2.5.1" + version "2.6.0" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' @@ -225,8 +225,10 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.google.android.gms:play-services-wearable:17.0.0' - implementation 'com.google.firebase:firebase-core:17.2.0' - implementation("com.crashlytics.sdk.android:crashlytics:2.9.9@aar") { + implementation 'com.google.firebase:firebase-core:17.2.1' + implementation 'com.google.firebase:firebase-auth:19.2.0' + implementation 'com.google.firebase:firebase-database:19.2.0' + implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') { transitive = true; } @@ -234,11 +236,11 @@ dependencies { 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.recyclerview:recyclerview:1.1.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" + implementation 'com.wdullaer:materialdatetimepicker:4.2.3' implementation "io.reactivex.rxjava2:rxandroid:2.1.1" @@ -247,11 +249,11 @@ dependencies { implementation("com.github.tony19:logback-android-classic:1.1.1-6") { exclude group: "com.google.android", module: "android" } - implementation "org.apache.commons:commons-lang3:3.7" - implementation "org.slf4j:slf4j-api:1.7.21" + implementation "org.apache.commons:commons-lang3:3.9" + implementation "org.slf4j:slf4j-api:1.7.29" // Graphview cannot be upgraded implementation "com.jjoe64:graphview:4.0.1" - implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.1.1" + implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2" 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' @@ -263,26 +265,29 @@ dependencies { // excluding org.json which is provided by Android exclude group: "org.json", module: "json" } - implementation "com.google.code.gson:gson:2.8.5" - implementation "com.google.guava:guava:24.1-jre" + implementation "com.google.code.gson:gson:2.8.6" + implementation ("com.google.guava:guava:24.1-jre") { + exclude group: "com.google.code.findbugs", module: "jsr305" + } + implementation 'com.google.code.findbugs:jsr305:3.0.2' - implementation "net.danlew:android.joda:2.9.9.1" - implementation "uk.com.robust-it:cloning:1.9.9" + implementation "net.danlew:android.joda:2.10.3" - implementation 'org.mozilla:rhino:1.7.7.2' + implementation 'org.mozilla:rhino:1.7.11' implementation 'com.github.DavidProdinger:weekdays-selector:1.1.0' testImplementation "junit:junit:4.12" - testImplementation "org.json:json:20140107" + testImplementation "org.json:json:20190722" testImplementation "org.mockito:mockito-core:2.8.47" testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}" - testImplementation "joda-time:joda-time:2.9.9" + testImplementation "joda-time:joda-time:2.10.5" testImplementation("com.google.truth:truth:0.39") { exclude group: "com.google.guava", module: "guava" + exclude group: "com.google.code.findbugs", module: "jsr305" } testImplementation "org.skyscreamer:jsonassert:1.5.0" testImplementation "org.hamcrest:hamcrest-all:1.3" @@ -292,20 +297,25 @@ dependencies { } */ - 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" + implementation 'com.squareup.okhttp3:okhttp:4.2.2' + implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2' + implementation "com.squareup.retrofit2:retrofit:2.6.2" + implementation "com.squareup.retrofit2:adapter-rxjava2:2.6.2" + implementation "com.squareup.retrofit2:converter-gson:2.6.2" + // Phone checker + implementation 'com.scottyab:rootbeer-lib:0.0.7' + + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha03' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test:rules:1.3.0-alpha03' + androidTestImplementation 'com.google.code.findbugs:jsr305:3.0.2' + androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' } @@ -332,7 +342,37 @@ task copyLibs(dependsOn: downloadAndUnzipFile, type: Copy) { task full_clean(type: Delete) { delete file("src/main/jniLibs") } - +/* +// Run 'adb' shell command to clear application data of main app for 'debug' variant +task clearMainAppData(type: Exec) { + // we have to iterate to find the 'debug' variant to obtain a variant reference + android.applicationVariants.all { variant -> + if (variant.name == "fullDebug") { + def applicationId = [variant.mergedFlavor.applicationId, variant.buildType.applicationIdSuffix].findAll().join() + def clearDataCommand = ['adb', 'shell', 'pm', 'clear', applicationId] + println "Clearing application data of ${variant.name} variant: [${clearDataCommand}]" + def stdout = new ByteArrayOutputStream() + exec { + commandLine clearDataCommand + standardOutput = stdout + } + String result = stdout.toString().trim() + if (!result.startsWith("Success")) { + println result + throw new GradleException(clearDataCommand.join(" ")) + } + } + } +} +// Clear Application Data (once) before running instrumentation test +tasks.whenTaskAdded { task -> + // Both of these targets are equivalent today, although in future connectedCheck + // will also include connectedUiAutomatorTest (not implemented yet) + if(task.name == "connectedAndroidTest" || task.name == "connectedCheck"){ + task.dependsOn(clearMainAppData) + } +} +*/ clean.dependsOn full_clean preBuild.dependsOn copyLibs diff --git a/app/google-services.json b/app/google-services.json index 42db6f4289..507c792c93 100644 --- a/app/google-services.json +++ b/app/google-services.json @@ -13,7 +13,12 @@ "package_name": "info.nightscout.aapspumpcontrol" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "477603612366-a925drvlvs7qn7gt73r585erbqto8c79.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyDcZpDRMaGjdhihXp531cVYM6LkEL8KbgM" @@ -37,7 +42,12 @@ "package_name": "info.nightscout.androidaps" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "477603612366-a925drvlvs7qn7gt73r585erbqto8c79.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyDcZpDRMaGjdhihXp531cVYM6LkEL8KbgM" @@ -61,7 +71,12 @@ "package_name": "info.nightscout.nsclient" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "477603612366-a925drvlvs7qn7gt73r585erbqto8c79.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyDcZpDRMaGjdhihXp531cVYM6LkEL8KbgM" @@ -85,7 +100,12 @@ "package_name": "info.nightscout.nsclient2" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "477603612366-a925drvlvs7qn7gt73r585erbqto8c79.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyDcZpDRMaGjdhihXp531cVYM6LkEL8KbgM" diff --git a/app/libs/ustwo-clockwise-debug.aar b/app/libs/ustwo-clockwise-debug.aar new file mode 100644 index 0000000000..8257a991be Binary files /dev/null and b/app/libs/ustwo-clockwise-debug.aar differ diff --git a/app/src/androidTest/java/info/nightscout/androidaps/ApplicationTest.java b/app/src/androidTest/java/info/nightscout/androidaps/ApplicationTest.java deleted file mode 100644 index a047e606a9..0000000000 --- a/app/src/androidTest/java/info/nightscout/androidaps/ApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/info/nightscout/androidaps/EspressoHelper.kt b/app/src/androidTest/java/info/nightscout/androidaps/EspressoHelper.kt new file mode 100644 index 0000000000..cf7118ebd2 --- /dev/null +++ b/app/src/androidTest/java/info/nightscout/androidaps/EspressoHelper.kt @@ -0,0 +1,35 @@ +package info.nightscout.androidaps + +import androidx.test.espresso.ViewAction +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiSelector + +fun ViewInteraction.isDisplayed(): Boolean { + try { + check(matches(ViewMatchers.isDisplayed())) + return true + } catch (e: Throwable) { + return false + } +} + +fun ViewInteraction.waitAndPerform(viewActions: ViewAction): ViewInteraction? { + val startTime = System.currentTimeMillis() + while (!isDisplayed()) { + Thread.sleep(100) + if (System.currentTimeMillis() - startTime >= 5000) { + throw AssertionError("View not visible after 5000 milliseconds") + } + } + return perform(viewActions) +} + +fun clickOkInDialog() { + val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + val button = uiDevice.findObject(UiSelector().clickable(true).checkable(false).index(1)) + if (button.exists() && button.isEnabled) button.click() +} diff --git a/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt b/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt new file mode 100644 index 0000000000..5463fbcd26 --- /dev/null +++ b/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt @@ -0,0 +1,116 @@ +package info.nightscout.androidaps + +import android.os.SystemClock +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import androidx.test.rule.ActivityTestRule +import androidx.test.rule.GrantPermissionRule +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.PumpInterface +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin +import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin +import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin +import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin +import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin +import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin +import info.nightscout.androidaps.plugins.source.RandomBgPlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.SP +import info.nightscout.androidaps.utils.isRunningTest +import org.json.JSONObject +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.slf4j.LoggerFactory + +@LargeTest +@RunWith(AndroidJUnit4::class) +class RealPumpTest { + + private val log = LoggerFactory.getLogger(L.CORE) + + companion object { + val pump: PumpInterface = DanaRv2Plugin.getPlugin() + const val R_PASSWORD = 1234 + const val R_SERIAL = "PBB00013LR_P" + } + + private val validProfile = "{\"dia\":\"6\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"10\"},{\"time\":\"2:00\",\"value\":\"11\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(MainActivity::class.java) + + @Rule + @JvmField + var mGrantPermissionRule: GrantPermissionRule = + GrantPermissionRule.grant( + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, + android.Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + @Before + fun clear() { + SP.clear() + SP.putBoolean(R.string.key_setupwizard_processed, true) + SP.putString(R.string.key_aps_mode, "closed") + MainApp.getDbHelper().resetDatabases() + MainApp.devBranch = false + } + + private fun preparePlugins() { + // Source + RandomBgPlugin.performPluginSwitch(true, PluginType.BGSOURCE) + // Profile + LocalProfilePlugin.performPluginSwitch(true, PluginType.PROFILE) + val profile = Profile(JSONObject(validProfile), Constants.MGDL) + Assert.assertTrue(profile.isValid("Test")) + LocalProfilePlugin.profiles.clear() + LocalProfilePlugin.numOfProfiles = 0 + val singleProfile = LocalProfilePlugin.SingleProfile().copyFrom(profile, "TestProfile") + LocalProfilePlugin.addProfile(singleProfile) + ProfileFunctions.doProfileSwitch(LocalProfilePlugin.createProfileStore(), "TestProfile", 0, 100, 0, DateUtil.now()) + // Insulin + InsulinOrefUltraRapidActingPlugin.getPlugin().performPluginSwitch(true, PluginType.INSULIN) + // Pump + SP.putInt(R.string.key_danar_password, R_PASSWORD) + SP.putString(R.string.key_danar_bt_name, R_SERIAL) + (pump as PluginBase).performPluginSwitch(true, PluginType.PUMP) + // Sensitivity + SensitivityOref1Plugin.getPlugin().performPluginSwitch(true, PluginType.SENSITIVITY) + // APS + OpenAPSSMBPlugin.getPlugin().performPluginSwitch(true, PluginType.APS) + LoopPlugin.getPlugin().performPluginSwitch(true, PluginType.LOOP) + + // Enable common + ActionsPlugin.performPluginSwitch(true, PluginType.GENERAL) + + // Disable unneeded + MainApp.getPluginsList().remove(ObjectivesPlugin) + } + + @Test + fun doTest() { + Assert.assertTrue(isRunningTest()) + preparePlugins() + + while (!pump.isInitialized) { + log.debug("Waiting for initialization") + SystemClock.sleep(1000) + } + + while (true) { + log.debug("Tick") + SystemClock.sleep(1000) + } + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt b/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt new file mode 100644 index 0000000000..a2742c2a34 --- /dev/null +++ b/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt @@ -0,0 +1,231 @@ +package info.nightscout.androidaps + +import android.os.SystemClock +import android.view.View +import android.view.ViewGroup +import androidx.test.espresso.Espresso.onData +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withClassName +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withTagValue +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import androidx.test.rule.ActivityTestRule +import androidx.test.rule.GrantPermissionRule +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin +import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin +import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin +import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin +import info.nightscout.androidaps.plugins.source.RandomBgPlugin +import info.nightscout.androidaps.setupwizard.SetupWizardActivity +import info.nightscout.androidaps.utils.HardLimits +import info.nightscout.androidaps.utils.SP +import info.nightscout.androidaps.utils.isRunningTest +import org.hamcrest.CoreMatchers.allOf +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.Matchers +import org.hamcrest.TypeSafeMatcher +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@LargeTest +@RunWith(AndroidJUnit4::class) +class SetupWizardActivityTest { + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(SetupWizardActivity::class.java) + + @Rule + @JvmField + var mGrantPermissionRule: GrantPermissionRule = + GrantPermissionRule.grant( + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, + android.Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + @Before + fun clear() { + SP.clear() + } +/* + +To run from command line +gradlew connectedFullDebugAndroidTest + +do not run when your production phone is connected !!! + +do this before for running in emulator +adb shell settings put global window_animation_scale 0 & +adb shell settings put global transition_animation_scale 0 & +adb shell settings put global animator_duration_scale 0 & + */ + + @Test + fun setupWizardActivityTest() { + SP.clear() + Assert.assertTrue(isRunningTest()) + // Welcome page + onView(withId(R.id.next_button)).perform(click()) + // Language selection + onView(withText("English")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Agreement page + onView(withText("I UNDERSTAND AND AGREE")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Location permission + var askButton = onView(withText("Ask for permission")) + if (askButton.isDisplayed()) { + askButton.perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + } + // Store permission + askButton = onView(withText("Ask for permission")) + if (askButton.isDisplayed()) { + askButton.perform(scrollTo(), click()) + onView(withText("OK")).perform(click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + } + // Import settings : skip of found + askButton = onView(withText("IMPORT SETTINGS")) + if (askButton.isDisplayed()) { + onView(withId(R.id.next_button)).waitAndPerform(click()) + } + // Units selection + onView(withText("mmol/L")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).perform(click()) + // Display target selection + onView(withText("4.2")).perform(scrollTo(), ViewActions.replaceText("5")) + onView(withText("10.0")).perform(scrollTo(), ViewActions.replaceText("11")) + onView(withId(R.id.next_button)).perform(click()) + // NSClient + onView(withId(R.id.next_button)).perform(click()) + // Age selection + onView(withText("Adult")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Insulin selection + onView(withText("Ultra-Rapid Oref")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // BG source selection + onView(withText("Random BG")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Profile selection + onView(withText("Local Profile")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Local profile - DIA + onView(withTagValue(Matchers.`is`("LP_DIA"))).perform(scrollTo(), ViewActions.replaceText("6.0")) + // Local profile - IC + onView(withId(R.id.ic_tab)).perform(scrollTo(), click()) + onView(Matchers.allOf(withTagValue(Matchers.`is`("IC-1-0")), isDisplayed())) + .perform(ViewActions.replaceText("2"), ViewActions.closeSoftKeyboard()) + // Local profile - ISF + onView(withId(R.id.isf_tab)).perform(scrollTo(), click()) + onView(Matchers.allOf(withTagValue(Matchers.`is`("ISF-1-0")), isDisplayed())) + .perform(ViewActions.replaceText("3"), ViewActions.closeSoftKeyboard()) + // Local profile - BAS + onView(withId(R.id.basal_tab)).perform(scrollTo(), click()) + onView(childAtPosition(Matchers.allOf(withId(R.id.localprofile_basal), childAtPosition(withClassName(Matchers.`is`("android.widget.LinearLayout")), 6)), 2)) + .perform(scrollTo(), click()) + onView(Matchers.allOf(withTagValue(Matchers.`is`("BASAL-1-0")), isDisplayed())) + .perform(ViewActions.replaceText("1.1"), ViewActions.closeSoftKeyboard()) + onView(Matchers.allOf(withTagValue(Matchers.`is`("BASAL-1-1")), isDisplayed())) + .perform(ViewActions.replaceText("1.2"), ViewActions.closeSoftKeyboard()) + onView(Matchers.allOf(withId(R.id.timelistedit_time), childAtPosition(childAtPosition(withId(R.id.localprofile_basal), 2), 0))) + .perform(scrollTo(), click()) + onData(Matchers.anything()).inAdapterView(childAtPosition(withClassName(Matchers.`is`("android.widget.PopupWindow\$PopupBackgroundView")), 0)).atPosition(13) + .perform(click()) + // Local profile - TARGET + onView(withId(R.id.target_tab)).perform(scrollTo(), click()) + onView(Matchers.allOf(withTagValue(Matchers.`is`("TARGET-1-0")), isDisplayed())) + .perform(ViewActions.replaceText("6"), ViewActions.closeSoftKeyboard()) + onView(Matchers.allOf(withTagValue(Matchers.`is`("TARGET-2-0")), isDisplayed())) + .perform(ViewActions.replaceText("6.5"), ViewActions.closeSoftKeyboard()) + onView(withText("Save")).perform(scrollTo(), click()) + onView(Matchers.allOf(withId(R.id.localprofile_profileswitch), isDisplayed())) + .perform(scrollTo(), click()) + onView(allOf(withId(R.id.ok), isDisplayed())).perform(click()) + // confirm dialog + //onView(Matchers.allOf(withText("OK"), isDisplayed())).perform(click()) not working on real phone + clickOkInDialog() + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Profile switch + askButton = onView(withText("Do Profile Switch")) + if (askButton.isDisplayed()) { + askButton.perform(scrollTo(), click()) + onView(allOf(withId(R.id.ok), isDisplayed())).perform(click()) + // onView(Matchers.allOf(withText("OK"), isDisplayed())).perform(click()) not working on real phone + clickOkInDialog() + while (ProfileFunctions.getInstance().profile == null) SystemClock.sleep(100) + onView(withId(R.id.next_button)).waitAndPerform(click()) + } + // Pump + onView(withText("Virtual Pump")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // APS + onView(withText("OpenAPS SMB")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Open Closed Loop + onView(withText("Closed Loop")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Loop + askButton = onView(withText("Enable loop")) + if (askButton.isDisplayed()) { + askButton.perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + } + // Sensitivity + onView(withText("Sensitivity Oref1")).perform(scrollTo(), click()) + onView(withId(R.id.next_button)).waitAndPerform(click()) + // Objectives + onView(allOf(withText("Start"), isDisplayed())).perform(scrollTo(), click()) + onView(withId(R.id.finish_button)).waitAndPerform(click()) + + // Verify settings + Assert.assertEquals(Constants.MMOL, ProfileFunctions.getSystemUnits()) + Assert.assertEquals(17.0, HardLimits.maxBolus(), 0.0001) // Adult + Assert.assertTrue(RandomBgPlugin.isEnabled(PluginType.BGSOURCE)) + Assert.assertTrue(LocalProfilePlugin.isEnabled(PluginType.PROFILE)) + val p = ProfileFunctions.getInstance().profile + Assert.assertNotNull(p) + Assert.assertEquals(2.0, p!!.ic, 0.0001) + Assert.assertEquals(3.0 * Constants.MMOLL_TO_MGDL, p.isfMgdl, 0.0001) + Assert.assertEquals(1.1, p.getBasalTimeFromMidnight(0), 0.0001) + Assert.assertEquals(6.0 * Constants.MMOLL_TO_MGDL, p.targetLowMgdl, 0.0001) + Assert.assertTrue(VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP)) + Assert.assertTrue(OpenAPSSMBPlugin.getPlugin().isEnabled(PluginType.APS)) + Assert.assertTrue(LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) + Assert.assertTrue(SensitivityOref1Plugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) + Assert.assertTrue(ObjectivesPlugin.objectives[0].isStarted) + } + + private fun childAtPosition( + parentMatcher: Matcher, position: Int): Matcher { + + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("Child at position $position in parent ") + parentMatcher.describeTo(description) + } + + public override fun matchesSafely(view: View): Boolean { + val parent = view.parent + return parent is ViewGroup && parentMatcher.matches(parent) + && view == parent.getChildAt(position) + } + } + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aeeb53475d..b571e247c2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -51,12 +51,11 @@ - @@ -76,6 +75,8 @@ + + @@ -122,16 +123,6 @@ - - - - - - - diff --git a/app/src/main/java/info/nightscout/androidaps/Constants.java b/app/src/main/java/info/nightscout/androidaps/Constants.java index 21a89d8920..fb322a8239 100644 --- a/app/src/main/java/info/nightscout/androidaps/Constants.java +++ b/app/src/main/java/info/nightscout/androidaps/Constants.java @@ -25,8 +25,6 @@ public class Constants { public static final int hoursToKeepInDatabase = 72; public static final int daysToKeepHistoryInDatabase = 30; - public static final long keepAliveMsecs = 5 * 60 * 1000L; - // SMS COMMUNICATOR public static final long remoteBolusMinDistance = 15 * 60 * 1000L; @@ -78,4 +76,15 @@ public class Constants { //Storage [MB] public static final long MINIMUM_FREE_SPACE = 200; + // Overview + public static final double LOWMARK = 76.0; + public static final double HIGHMARK = 180.0; + + // STATISTICS + public static final double STATS_TARGET_LOW_MMOL = 3.9; + public static final double STATS_TARGET_HIGH_MMOL = 7.8; + public static final double STATS_RANGE_LOW_MMOL = 3.9; + public static final double STATS_RANGE_HIGH_MMOL = 10.0; + + } diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 64c2656086..c152c415cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -37,12 +37,11 @@ import com.joanzapata.iconify.fonts.FontAwesomeModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import info.nightscout.androidaps.activities.AgreementActivity; import info.nightscout.androidaps.activities.HistoryBrowseActivity; import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.activities.PreferencesActivity; import info.nightscout.androidaps.activities.SingleFragmentActivity; -import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.activities.StatsActivity; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRebuildTabs; @@ -51,7 +50,6 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtilsKt; import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus; import info.nightscout.androidaps.setupwizard.SetupWizardActivity; @@ -65,6 +63,8 @@ import info.nightscout.androidaps.utils.SP; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; +import static info.nightscout.androidaps.utils.EspressoTestHelperKt.isRunningRealPumpTest; + public class MainActivity extends NoSplashAppCompatActivity { private static Logger log = LoggerFactory.getLogger(L.CORE); private CompositeDisposable disposable = new CompositeDisposable(); @@ -94,8 +94,6 @@ public class MainActivity extends NoSplashAppCompatActivity { // initialize screen wake lock processPreferenceChange(new EventPreferenceChange(R.string.key_keep_screen_on)); - doMigrations(); - final ViewPager viewPager = findViewById(R.id.pager); viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override @@ -141,11 +139,9 @@ public class MainActivity extends NoSplashAppCompatActivity { .subscribe(this::processPreferenceChange, FabricPrivacy::logException) ); - if (!SP.getBoolean(R.string.key_setupwizard_processed, false)) { + if (!SP.getBoolean(R.string.key_setupwizard_processed, false) && !isRunningRealPumpTest()) { Intent intent = new Intent(this, SetupWizardActivity.class); startActivity(intent); - } else { - checkEula(); } AndroidPermission.notifyForStoragePermission(this); @@ -153,6 +149,7 @@ public class MainActivity extends NoSplashAppCompatActivity { if (Config.PUMPDRIVERS) { AndroidPermission.notifyForLocationPermissions(this); AndroidPermission.notifyForSMSPermissions(this); + AndroidPermission.notifyForSystemWindowPermissions(this); } } @@ -237,49 +234,6 @@ public class MainActivity extends NoSplashAppCompatActivity { } } - private void checkEula() { - //SP.removeBoolean(R.string.key_i_understand); - boolean IUnderstand = SP.getBoolean(R.string.key_i_understand, false); - if (!IUnderstand) { - Intent intent = new Intent(getApplicationContext(), AgreementActivity.class); - startActivity(intent); - finish(); - } - } - - private void doMigrations() { - - checkUpgradeToProfileTarget(); - - // guarantee that the unreachable threshold is at least 30 and of type String - // Added in 1.57 at 21.01.2018 - int unreachable_threshold = SP.getInt(R.string.key_pump_unreachable_threshold, 30); - SP.remove(R.string.key_pump_unreachable_threshold); - if (unreachable_threshold < 30) unreachable_threshold = 30; - SP.putString(R.string.key_pump_unreachable_threshold, Integer.toString(unreachable_threshold)); - } - - - private void checkUpgradeToProfileTarget() { // TODO: can be removed in the future - boolean oldKeyExists = SP.contains("openapsma_min_bg"); - if (oldKeyExists) { - Profile profile = ProfileFunctions.getInstance().getProfile(); - String oldRange = SP.getDouble("openapsma_min_bg", 0d) + " - " + SP.getDouble("openapsma_max_bg", 0d); - String newRange = ""; - if (profile != null) { - newRange = profile.getTargetLow() + " - " + profile.getTargetHigh(); - } - String message = "Target range is changed in current version.\n\nIt's not taken from preferences but from profile.\n\n!!! REVIEW YOUR SETTINGS !!!"; - message += "\n\nOld settings: " + oldRange; - message += "\nProfile settings: " + newRange; - OKDialog.show(this, "Target range change", message, () -> { - SP.remove("openapsma_min_bg"); - SP.remove("openapsma_max_bg"); - SP.remove("openapsma_target_bg"); - }); - } - } - @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); @@ -288,15 +242,13 @@ public class MainActivity extends NoSplashAppCompatActivity { switch (requestCode) { case AndroidPermission.CASE_STORAGE: //show dialog after permission is granted - AlertDialog.Builder alert = new AlertDialog.Builder(this); - alert.setMessage(R.string.alert_dialog_storage_permission_text); - alert.setPositiveButton(R.string.ok, null); - alert.show(); + OKDialog.show(this, "", MainApp.gs(R.string.alert_dialog_storage_permission_text)); break; case AndroidPermission.CASE_LOCATION: case AndroidPermission.CASE_SMS: case AndroidPermission.CASE_BATTERY: case AndroidPermission.CASE_PHONE_STATE: + case AndroidPermission.CASE_SYSTEM_WINDOW: break; } } @@ -365,9 +317,7 @@ public class MainActivity extends NoSplashAppCompatActivity { return true; case R.id.nav_exit: log.debug("Exiting"); - MainApp.instance().stopKeepAliveService(); RxBus.INSTANCE.send(new EventAppExit()); - MainApp.closeDbHelper(); finish(); System.runFinalization(); System.exit(0); @@ -381,6 +331,14 @@ public class MainActivity extends NoSplashAppCompatActivity { startActivity(i); }, null); return true; +/* + case R.id.nav_survey: + startActivity(new Intent(this, SurveyActivity.class)); + return true; +*/ + case R.id.nav_stats: + startActivity(new Intent(this, StatsActivity.class)); + return true; } return actionBarDrawerToggle.onOptionsItemSelected(item); } diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 379afabddf..22df82d212 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -1,12 +1,14 @@ package info.nightscout.androidaps; import android.app.Application; -import android.content.BroadcastReceiver; import android.content.IntentFilter; import android.content.res.Resources; import android.os.SystemClock; +import androidx.annotation.ColorRes; import androidx.annotation.PluralsRes; +import androidx.annotation.StringRes; +import androidx.core.content.ContextCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.crashlytics.android.Crashlytics; @@ -15,6 +17,7 @@ import com.j256.ormlite.android.apptools.OpenHelperManager; import net.danlew.android.joda.JodaTimeAndroid; +import org.json.JSONException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,6 +25,7 @@ import java.io.File; import java.util.ArrayList; import info.nightscout.androidaps.data.ConstraintChecker; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; @@ -32,6 +36,7 @@ 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.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.constraints.dstHelper.DstHelperPlugin; import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; @@ -46,8 +51,6 @@ import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils; import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin; import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.nsclient.receivers.AckAlarmReceiver; -import info.nightscout.androidaps.plugins.general.nsclient.receivers.DBAccessReceiver; import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin; import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; @@ -59,7 +62,6 @@ import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlu import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin; import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; -import info.nightscout.androidaps.plugins.profile.simple.SimpleProfilePlugin; import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin; import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin; @@ -73,6 +75,7 @@ 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.RandomBgPlugin; import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin; import info.nightscout.androidaps.plugins.source.SourceEversensePlugin; import info.nightscout.androidaps.plugins.source.SourceGlimpPlugin; @@ -87,8 +90,10 @@ 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.ActivityMonitor; import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.LocaleHelper; +import info.nightscout.androidaps.utils.SP; import io.fabric.sdk.android.Fabric; import static info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtilsKt.triggerCheckVersion; @@ -96,7 +101,6 @@ import static info.nightscout.androidaps.plugins.constraints.versionChecker.Vers public class MainApp extends Application { private static Logger log = LoggerFactory.getLogger(L.CORE); - private static KeepAliveReceiver keepAliveReceiver; private static MainApp sInstance; public static Resources sResources; @@ -109,11 +113,7 @@ public class MainApp extends Application { private static ArrayList pluginsList = null; private static DataReceiver dataReceiver = new DataReceiver(); - private static NSAlarmReceiver alarmReciever = new NSAlarmReceiver(); - private static AckAlarmReceiver ackAlarmReciever = new AckAlarmReceiver(); - private static DBAccessReceiver dbAccessReciever = new DBAccessReceiver(); - private LocalBroadcastManager lbm; - BroadcastReceiver btReceiver; + private static NSAlarmReceiver alarmReceiver = new NSAlarmReceiver(); TimeDateOrTZChangeReceiver timeDateOrTZChangeReceiver; public static boolean devBranch; @@ -129,7 +129,14 @@ public class MainApp extends Application { sConstraintsChecker = new ConstraintChecker(); sDatabaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class); - Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> log.error("Uncaught exception crashing app", ex)); + Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { + if (ex instanceof InternalError) { + // usually the app trying to spawn a thread while being killed + return; + } + + log.error("Uncaught exception crashing app", ex); + }); try { if (FabricPrivacy.fabricEnabled()) { @@ -139,6 +146,8 @@ public class MainApp extends Application { log.error("Error with Fabric init! " + e); } + registerActivityLifecycleCallbacks(ActivityMonitor.INSTANCE); + mFirebaseAnalytics = FirebaseAnalytics.getInstance(this); mFirebaseAnalytics.setAnalyticsCollectionEnabled(!Boolean.getBoolean("disableFirebase")); @@ -158,7 +167,6 @@ 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<>(); @@ -182,14 +190,13 @@ public class MainApp extends Application { if (Config.PUMPDRIVERS) pluginsList.add(MedtronicPumpPlugin.getPlugin()); if (!Config.NSCLIENT) pluginsList.add(MDIPlugin.getPlugin()); pluginsList.add(VirtualPumpPlugin.getPlugin()); - pluginsList.add(CareportalPlugin.getPlugin()); + if (Config.NSCLIENT) pluginsList.add(CareportalPlugin.getPlugin()); if (Config.APS) pluginsList.add(LoopPlugin.getPlugin()); if (Config.APS) pluginsList.add(OpenAPSMAPlugin.getPlugin()); if (Config.APS) pluginsList.add(OpenAPSAMAPlugin.getPlugin()); if (Config.APS) pluginsList.add(OpenAPSSMBPlugin.getPlugin()); pluginsList.add(NSProfilePlugin.getPlugin()); - if (!Config.NSCLIENT) pluginsList.add(SimpleProfilePlugin.getPlugin()); - if (!Config.NSCLIENT) pluginsList.add(LocalProfilePlugin.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(LocalProfilePlugin.INSTANCE); pluginsList.add(TreatmentsPlugin.getPlugin()); if (!Config.NSCLIENT) pluginsList.add(SafetyPlugin.getPlugin()); if (!Config.NSCLIENT) pluginsList.add(VersionCheckerPlugin.INSTANCE); @@ -204,7 +211,8 @@ public class MainApp extends Application { pluginsList.add(SourcePoctechPlugin.getPlugin()); pluginsList.add(SourceTomatoPlugin.getPlugin()); pluginsList.add(SourceEversensePlugin.getPlugin()); - if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.getPlugin()); + pluginsList.add(RandomBgPlugin.INSTANCE); + if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.INSTANCE); pluginsList.add(FoodPlugin.getPlugin()); pluginsList.add(WearPlugin.initPlugin(this)); @@ -230,13 +238,40 @@ public class MainApp extends Application { new Thread(() -> { SystemClock.sleep(5000); ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("Initialization", null); - startKeepAliveService(); }).start(); } + + new Thread(() -> KeepAliveReceiver.setAlarm(this)).start(); + doMigrations(); + } + + private void doMigrations() { + + // guarantee that the unreachable threshold is at least 30 and of type String + // Added in 1.57 at 21.01.2018 + int unreachable_threshold = SP.getInt(R.string.key_pump_unreachable_threshold, 30); + SP.remove(R.string.key_pump_unreachable_threshold); + if (unreachable_threshold < 30) unreachable_threshold = 30; + SP.putString(R.string.key_pump_unreachable_threshold, Integer.toString(unreachable_threshold)); + + // 2.5 -> 2.6 + if (!SP.contains(R.string.key_units)) { + String newUnits = Constants.MGDL; + Profile p = ProfileFunctions.getInstance().getProfile(); + if (p != null && p.getData() != null && p.getData().has("units")) { + try { + newUnits = p.getData().getString("units"); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + } + SP.putString(R.string.key_units, newUnits); + } } + private void registerLocalBroadcastReceiver() { - lbm = LocalBroadcastManager.getInstance(this); + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_NEW_TREATMENT)); lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_CHANGED_TREATMENT)); lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_REMOVED_TREATMENT)); @@ -251,39 +286,21 @@ public class MainApp extends Application { lbm.registerReceiver(dataReceiver, new IntentFilter(Intents.ACTION_NEW_CAL)); //register alarms - lbm.registerReceiver(alarmReciever, new IntentFilter(Intents.ACTION_ALARM)); - lbm.registerReceiver(alarmReciever, new IntentFilter(Intents.ACTION_ANNOUNCEMENT)); - lbm.registerReceiver(alarmReciever, new IntentFilter(Intents.ACTION_CLEAR_ALARM)); - lbm.registerReceiver(alarmReciever, new IntentFilter(Intents.ACTION_URGENT_ALARM)); - - //register ack alarm - lbm.registerReceiver(ackAlarmReciever, new IntentFilter(Intents.ACTION_ACK_ALARM)); - - //register dbaccess - lbm.registerReceiver(dbAccessReciever, new IntentFilter(Intents.ACTION_DATABASE)); + lbm.registerReceiver(alarmReceiver, new IntentFilter(Intents.ACTION_ALARM)); + lbm.registerReceiver(alarmReceiver, new IntentFilter(Intents.ACTION_ANNOUNCEMENT)); + lbm.registerReceiver(alarmReceiver, new IntentFilter(Intents.ACTION_CLEAR_ALARM)); + lbm.registerReceiver(alarmReceiver, new IntentFilter(Intents.ACTION_URGENT_ALARM)); this.timeDateOrTZChangeReceiver = new TimeDateOrTZChangeReceiver(); this.timeDateOrTZChangeReceiver.registerBroadcasts(this); } - private void startKeepAliveService() { - if (keepAliveReceiver == null) { - keepAliveReceiver = new KeepAliveReceiver(); - keepAliveReceiver.setAlarm(this); - } - } - - public void stopKeepAliveService() { - if (keepAliveReceiver != null) - KeepAliveReceiver.cancelAlarm(this); - } - - public static String gs(int id) { + public static String gs(@StringRes int id) { return sResources.getString(id); } - public static String gs(int id, Object... args) { + public static String gs(@StringRes int id, Object... args) { return sResources.getString(id, args); } @@ -291,8 +308,8 @@ public class MainApp extends Application { return sResources.getQuantityString(id, quantity, args); } - public static int gc(int id) { - return sResources.getColor(id); + public static int gc(@ColorRes int id) { + return ContextCompat.getColor(instance(), id); } public static MainApp instance() { @@ -303,13 +320,6 @@ public class MainApp extends Application { return sDatabaseHelper; } - public static void closeDbHelper() { - if (sDatabaseHelper != null) { - sDatabaseHelper.close(); - sDatabaseHelper = null; - } - } - public static FirebaseAnalytics getFirebaseAnalytics() { return mFirebaseAnalytics; } @@ -412,20 +422,12 @@ public class MainApp extends Application { public void onTerminate() { if (L.isEnabled(L.CORE)) log.debug("onTerminate"); - super.onTerminate(); - if (sDatabaseHelper != null) { - sDatabaseHelper.close(); - sDatabaseHelper = null; - } - if (btReceiver != null) { - unregisterReceiver(btReceiver); - } - - if (timeDateOrTZChangeReceiver != null) { + if (timeDateOrTZChangeReceiver != null) unregisterReceiver(timeDateOrTZChangeReceiver); - } - + unregisterActivityLifecycleCallbacks(ActivityMonitor.INSTANCE); + KeepAliveReceiver.cancelAlarm(this); + super.onTerminate(); } public static int dpToPx(int dp) { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/AgreementActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/AgreementActivity.java deleted file mode 100644 index d3792bbad5..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/activities/AgreementActivity.java +++ /dev/null @@ -1,44 +0,0 @@ -package info.nightscout.androidaps.activities; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; - -import info.nightscout.androidaps.MainActivity; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.utils.SP; - -public class AgreementActivity extends NoSplashActivity { - boolean IUnderstand; - CheckBox agreeCheckBox; - Button saveButton; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_agreement); - IUnderstand = SP.getBoolean(R.string.key_i_understand, false); - setContentView(R.layout.activity_agreement); - agreeCheckBox = (CheckBox)findViewById(R.id.agreementCheckBox); - agreeCheckBox.setChecked(IUnderstand); - saveButton = (Button)findViewById(R.id.agreementSaveButton); - addListenerOnButton(); - } - - public void addListenerOnButton() { - saveButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - - SP.putBoolean(R.string.key_i_understand, agreeCheckBox.isChecked()); - - Intent intent = new Intent(getApplicationContext(), MainActivity.class); - startActivity(intent); - finish(); - } - - }); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/BolusProgressHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/BolusProgressHelperActivity.kt new file mode 100644 index 0000000000..fe55cdcdf2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/activities/BolusProgressHelperActivity.kt @@ -0,0 +1,14 @@ +package info.nightscout.androidaps.activities + +import android.os.Bundle +import info.nightscout.androidaps.dialogs.BolusProgressDialog + +class BolusProgressHelperActivity : DialogAppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + BolusProgressDialog() + .setHelperActivity(this) + .setInsulin(intent.getDoubleExtra("insulin", 0.0)) + .show(supportFragmentManager, "BolusProgress") + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/activities/DialogAppCompatActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/DialogAppCompatActivity.kt new file mode 100644 index 0000000000..a1c6dd1236 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/activities/DialogAppCompatActivity.kt @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.activities + +import android.content.Context +import androidx.appcompat.app.AppCompatActivity +import info.nightscout.androidaps.utils.LocaleHelper + +open class DialogAppCompatActivity : AppCompatActivity() { + public override fun attachBaseContext(newBase: Context) { + super.attachBaseContext(LocaleHelper.wrap(newBase)) + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/ErrorHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/ErrorHelperActivity.kt similarity index 80% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/ErrorHelperActivity.kt rename to app/src/main/java/info/nightscout/androidaps/activities/ErrorHelperActivity.kt index 15840d3499..1e9ac6c254 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/ErrorHelperActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/ErrorHelperActivity.kt @@ -1,12 +1,12 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs +package info.nightscout.androidaps.activities import android.os.Bundle import info.nightscout.androidaps.R -import info.nightscout.androidaps.activities.NoSplashAppCompatActivity +import info.nightscout.androidaps.dialogs.ErrorDialog import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.utils.SP -class ErrorHelperActivity : NoSplashAppCompatActivity() { +class ErrorHelperActivity : DialogAppCompatActivity() { @Override override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.java index 79f3fbf4d4..c0f654bbbb 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.java @@ -45,7 +45,7 @@ import info.nightscout.androidaps.utils.T; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; -public class HistoryBrowseActivity extends NoSplashActivity { +public class HistoryBrowseActivity extends NoSplashAppCompatActivity { private static Logger log = LoggerFactory.getLogger(HistoryBrowseActivity.class); private CompositeDisposable disposable = new CompositeDisposable(); @@ -150,7 +150,7 @@ public class HistoryBrowseActivity extends NoSplashActivity { ); dpd.setThemeDark(true); dpd.dismissOnPause(true); - dpd.show(getFragmentManager(), "Datepickerdialog"); + dpd.show(getSupportFragmentManager(), "Datepickerdialog"); }); bgGraph.getGridLabelRenderer().setGridColor(MainApp.gc(R.color.graphgrid)); @@ -231,9 +231,8 @@ public class HistoryBrowseActivity extends NoSplashActivity { noProfile.setVisibility(View.GONE); } - final String units = profile.getUnits(); - final double lowLine = OverviewPlugin.INSTANCE.determineLowLine(units); - final double highLine = OverviewPlugin.INSTANCE.determineHighLine(units); + final double lowLine = OverviewPlugin.INSTANCE.determineLowLine(); + final double highLine = OverviewPlugin.INSTANCE.determineHighLine(); buttonDate.setText(DateUtil.dateAndTimeString(start)); buttonZoom.setText(String.valueOf(rangeToDisplay)); diff --git a/app/src/main/java/info/nightscout/androidaps/activities/NoSplashActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashActivity.kt deleted file mode 100644 index 0b7af2bd1d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/activities/NoSplashActivity.kt +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps.activities - -import android.app.Activity -import android.os.Bundle - -import info.nightscout.androidaps.R - -open class NoSplashActivity : Activity() { - public override fun onCreate(savedInstanceState: Bundle?) { - setTheme(R.style.AppTheme_NoActionBar) - super.onCreate(savedInstanceState) - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt index e4c8027cd5..3f59cd56ef 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt @@ -1,12 +1,18 @@ package info.nightscout.androidaps.activities +import android.content.Context import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.LocaleHelper open class NoSplashAppCompatActivity : AppCompatActivity() { public override fun onCreate(savedInstanceState: Bundle?) { setTheme(R.style.AppTheme_NoActionBar) super.onCreate(savedInstanceState) } + + public override fun attachBaseContext(newBase: Context) { + super.attachBaseContext(LocaleHelper.wrap(newBase)) + } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java index 367429dbbf..328f143836 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.activities; +import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.EditTextPreference; @@ -9,27 +10,31 @@ import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.text.TextUtils; + +import java.util.Arrays; 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.data.Profile; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRebuildTabs; 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; 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.bus.RxBus; +import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; +import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; +import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin; +import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; +import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin; +import info.nightscout.androidaps.plugins.general.wear.WearPlugin; +import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin; +import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin; import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin; import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin; @@ -42,14 +47,11 @@ 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.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; +import info.nightscout.androidaps.utils.SafeParse; public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { MyPreferenceFragment myPreferenceFragment; @@ -66,21 +68,47 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); } + @Override + public void attachBaseContext(Context newBase) { + super.attachBaseContext(LocaleHelper.INSTANCE.wrap(newBase)); + } + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { RxBus.INSTANCE.send(new EventPreferenceChange(key)); - if (key.equals("language")) { + if (key.equals(MainApp.gs(R.string.key_language))) { RxBus.INSTANCE.send(new EventRebuildTabs(true)); //recreate() does not update language so better close settings finish(); } - if (key.equals("short_tabtitles")) { + if (key.equals(MainApp.gs(R.string.key_short_tabtitles))) { RxBus.INSTANCE.send(new EventRebuildTabs()); } - if (key.equals(MainApp.gs(R.string.key_openapsama_useautosens)) && SP.getBoolean(R.string.key_openapsama_useautosens, false)) { - OKDialog.show(this, MainApp.gs(R.string.configbuilder_sensitivity), MainApp.gs(R.string.sensitivity_warning), null); + if (key.equals(MainApp.gs(R.string.key_units))) { + recreate(); + return; + } + if (key.equals(MainApp.gs(R.string.key_openapsama_useautosens)) && SP.getBoolean(R.string.key_openapsama_useautosens, false)) { + OKDialog.show(this, MainApp.gs(R.string.configbuilder_sensitivity), MainApp.gs(R.string.sensitivity_warning)); + } + updatePrefSummary(myPreferenceFragment.findPreference(key)); + } + + private static void adjustUnitDependentPrefs(Preference pref) { + // convert preferences values to current units + String[] unitDependent = new String[]{ + MainApp.gs(R.string.key_hypo_target), + MainApp.gs(R.string.key_activity_target), + MainApp.gs(R.string.key_eatingsoon_target), + MainApp.gs(R.string.key_high_mark), + MainApp.gs(R.string.key_low_mark) + }; + if (Arrays.asList(unitDependent).contains(pref.getKey())) { + EditTextPreference editTextPref = (EditTextPreference) pref; + String converted = Profile.toCurrentUnitsString(SafeParse.stringToDouble(editTextPref.getText())); + editTextPref.setSummary(converted); + editTextPref.setText(converted); } - updatePrefSummary(myPreferenceFragment.getPreference(key)); } private static void updatePrefSummary(Preference pref) { @@ -92,15 +120,17 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre EditTextPreference editTextPref = (EditTextPreference) pref; if (pref.getKey().contains("password") || pref.getKey().contains("secret")) { pref.setSummary("******"); - } else if (pref.getKey().equals(MainApp.gs(R.string.key_danars_name))) { - pref.setSummary(SP.getString(R.string.key_danars_name, "")); } else if (editTextPref.getText() != null) { ((EditTextPreference) pref).setDialogMessage(editTextPref.getDialogMessage()); pref.setSummary(editTextPref.getText()); - } else if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) { - pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary)); + } else { + for (PluginBase plugin : MainApp.getPluginsList()) { + plugin.updatePreferenceSummary(pref); + } } } + if (pref != null) + adjustUnitDependentPrefs(pref); } public static void initSummary(Preference p) { @@ -143,8 +173,8 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre if (!Config.NSCLIENT) { addPreferencesFromResource(R.xml.pref_password); } + addPreferencesFromResource(R.xml.pref_general); addPreferencesFromResource(R.xml.pref_age); - addPreferencesFromResource(R.xml.pref_language); addPreferencesFromResource(R.xml.pref_overview); @@ -188,7 +218,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL); addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL); - addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL); + addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.INSTANCE, PluginType.GENERAL); addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL); addPreferencesFromResource(R.xml.pref_others); @@ -198,26 +228,11 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(StatuslinePlugin.getPlugin(), PluginType.GENERAL); } - if (Config.NSCLIENT) { - 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))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_bat_warning))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_bat_critical))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_show_statuslights))); - scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_show_statuslights_extended))); - } - } - 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; - }); + for (PluginBase plugin : MainApp.getPluginsList()) { + plugin.preprocessPreferences(this); + } } @Override @@ -225,9 +240,5 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre super.onSaveInstanceState(outState); outState.putInt("id", id); } - - public Preference getPreference(String key) { - return findPreference(key); - } } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt index 96a399a299..f50212dadd 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps.activities import android.os.Bundle import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin -class RequestDexcomPermissionActivity : NoSplashAppCompatActivity() { +class RequestDexcomPermissionActivity : DialogAppCompatActivity() { private val requestCode = "AndroidAPS <3".map { it.toInt() }.sum() diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java deleted file mode 100644 index 688ba82c6a..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java +++ /dev/null @@ -1,60 +0,0 @@ -package info.nightscout.androidaps.activities; - -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.utils.PasswordProtection; - -public class SingleFragmentActivity extends AppCompatActivity { - - private PluginBase plugin; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_single_fragment); - - this.plugin = MainApp.getPluginsList().get(getIntent().getIntExtra("plugin", -1)); - setTitle(plugin.getName()); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setDisplayShowHomeEnabled(true); - - if (savedInstanceState == null) { - getSupportFragmentManager().beginTransaction().replace(R.id.frame_layout, - Fragment.instantiate(this, plugin.pluginDescription.getFragmentClass())).commit(); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - finish(); - return true; - } - else if (item.getItemId() == R.id.nav_plugin_preferences) { - PasswordProtection.QueryPassword(this, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(this, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - startActivity(i); - }, null); - return true; - } - return false; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (plugin.getPreferencesId() != -1) - getMenuInflater().inflate(R.menu.menu_single_fragment, menu); - return super.onCreateOptionsMenu(menu); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.kt new file mode 100644 index 0000000000..3bf329debe --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.kt @@ -0,0 +1,53 @@ +package info.nightscout.androidaps.activities + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import androidx.appcompat.app.AppCompatActivity +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.utils.LocaleHelper +import info.nightscout.androidaps.utils.PasswordProtection + +class SingleFragmentActivity : AppCompatActivity() { + private var plugin: PluginBase? = null + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_single_fragment) + plugin = MainApp.getPluginsList()[intent.getIntExtra("plugin", -1)] + title = plugin?.name + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + if (savedInstanceState == null) { + supportFragmentManager.beginTransaction().replace(R.id.frame_layout, + supportFragmentManager.fragmentFactory.instantiate(ClassLoader.getSystemClassLoader(), plugin?.pluginDescription?.fragmentClass!!)).commit() + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + finish() + return true + } else if (item.itemId == R.id.nav_plugin_preferences) { + PasswordProtection.QueryPassword(this, R.string.settings_password, "settings_password", Runnable { + val i = Intent(this, PreferencesActivity::class.java) + i.putExtra("id", plugin?.preferencesId) + startActivity(i) + }, null) + return true + } + return false + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + if (plugin?.preferencesId != -1) menuInflater.inflate(R.menu.menu_single_fragment, menu) + return super.onCreateOptionsMenu(menu) + } + + public override fun attachBaseContext(newBase: Context) { + super.attachBaseContext(LocaleHelper.wrap(newBase)) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt new file mode 100644 index 0000000000..d0ac8e3f6f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.activities + +import android.os.Bundle +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.ActivityMonitor +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.TddCalculator +import info.nightscout.androidaps.utils.TirCalculator +import kotlinx.android.synthetic.main.stats_activity.* + +class StatsActivity : NoSplashAppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.stats_activity) + + stats_tdds.text = TddCalculator.stats() + stats_tir.text = TirCalculator.stats() + stats_activity.text = ActivityMonitor.stats() + + ok.setOnClickListener { finish() } + stats_reset.setOnClickListener { + OKDialog.showConfirmation(this, MainApp.gs(R.string.doyouwantresetstats), Runnable { + ActivityMonitor.reset() + recreate() + }) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt new file mode 100644 index 0000000000..bf07eda9af --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt @@ -0,0 +1,110 @@ +package info.nightscout.androidaps.activities + +import android.os.Bundle +import android.widget.ArrayAdapter +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.database.FirebaseDatabase +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.defaultProfile.DefaultProfile +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.dialogs.ProfileViewerDialog +import info.nightscout.androidaps.utils.* +import kotlinx.android.synthetic.main.survey_activity.* +import org.slf4j.LoggerFactory + +class SurveyActivity : NoSplashAppCompatActivity() { + private val log = LoggerFactory.getLogger(SurveyActivity::class.java) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.survey_activity) + + survey_id.text = InstanceId.instanceId() + + val profileStore = ConfigBuilderPlugin.getPlugin().activeProfileInterface?.profile + val profileList = profileStore?.getProfileList() ?: return + survey_spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList) + + survey_tdds.text = TddCalculator.stats() + survey_tir.text = TirCalculator.stats() + survey_activity.text = ActivityMonitor.stats() + + survey_profile.setOnClickListener { + val age = SafeParse.stringToDouble(survey_age.text.toString()) + val weight = SafeParse.stringToDouble(survey_weight.text.toString()) + val tdd = SafeParse.stringToDouble(survey_tdd.text.toString()) + if (age < 1 || age > 120) { + ToastUtils.showToastInUiThread(this, R.string.invalidage) + return@setOnClickListener + } + if ((weight < 5 || weight > 150) && tdd == 0.0) { + ToastUtils.showToastInUiThread(this, R.string.invalidweight) + return@setOnClickListener + } + if ((tdd < 5 || tdd > 150) && weight == 0.0) { + ToastUtils.showToastInUiThread(this, R.string.invalidweight) + return@setOnClickListener + } + val profile = DefaultProfile().profile(age, tdd, weight, ProfileFunctions.getSystemUnits()) + val args = Bundle() + args.putLong("time", DateUtil.now()) + args.putInt("mode", ProfileViewerDialog.Mode.CUSTOM_PROFILE.ordinal) + args.putString("customProfile", profile.data.toString()) + args.putString("customProfileUnits", profile.units) + args.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight") + val pvd = ProfileViewerDialog() + pvd.arguments = args + pvd.show(supportFragmentManager, "ProfileViewDialog") + } + + survey_submit.setOnClickListener { + val r = FirebaseRecord() + r.id = InstanceId.instanceId() + r.age = SafeParse.stringToInt(survey_age.text.toString()) + r.weight = SafeParse.stringToInt(survey_weight.text.toString()) + if (r.age < 1 || r.age > 120) { + ToastUtils.showToastInUiThread(this, R.string.invalidage) + return@setOnClickListener + } + if (r.weight < 5 || r.weight > 150) { + ToastUtils.showToastInUiThread(this, R.string.invalidweight) + return@setOnClickListener + } + + if (survey_spinner.selectedItem == null) + return@setOnClickListener + val profileName = survey_spinner.selectedItem.toString() + val specificProfile = profileStore.getSpecificProfile(profileName) + + r.profileJson = specificProfile.toString() + + val auth = FirebaseAuth.getInstance() + auth.signInAnonymously() + .addOnCompleteListener(this) { task -> + if (task.isSuccessful) { + log.debug("signInAnonymously:success") + val user = auth.currentUser // TODO: do we need this, seems unused? + + val database = FirebaseDatabase.getInstance().reference + database.child("survey").child(r.id).setValue(r) + } else { + log.error("signInAnonymously:failure", task.exception) + ToastUtils.showToastInUiThread(this, "Authentication failed.") + //updateUI(null) + } + + // ... + } + finish() + } + } + + inner class FirebaseRecord { + var id = "" + var age: Int = 0 + var weight: Int = 0 + var profileJson = "ghfg" + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/TDDStatsActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/TDDStatsActivity.java index 24cdceada6..03fb034c5c 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/TDDStatsActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/TDDStatsActivity.java @@ -54,7 +54,7 @@ import info.nightscout.androidaps.utils.SafeParse; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; -public class TDDStatsActivity extends NoSplashActivity { +public class TDDStatsActivity extends NoSplashAppCompatActivity { private static Logger log = LoggerFactory.getLogger(TDDStatsActivity.class); private CompositeDisposable disposable = new CompositeDisposable(); diff --git a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java index 79885e297f..60e1b21048 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java +++ b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java @@ -1,7 +1,5 @@ package info.nightscout.androidaps.data; -import com.rits.cloning.Cloner; - import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -37,8 +35,19 @@ public class IobTotal implements DataPointWithLabelInterface { public IobTotal copy() { - Cloner cloner = new Cloner(); - return cloner.deepClone(this); + IobTotal i = new IobTotal(time); + i.iob = iob; + i.activity = activity; + i.bolussnooze = bolussnooze; + i.basaliob = basaliob; + i.netbasalinsulin = netbasalinsulin; + i.hightempinsulin = hightempinsulin; + i.lastBolusTime = lastBolusTime; + if (iobWithZeroTemp != null) i.iobWithZeroTemp = iobWithZeroTemp.copy(); + i.netInsulin = netInsulin; + i.netRatio = netRatio; + i.extendedBolusInsulin = extendedBolusInsulin; + return i; } public IobTotal(long time) { @@ -137,7 +146,7 @@ public class IobTotal implements DataPointWithLabelInterface { // DataPoint interface - int color; + private int color; @Override public double getX() { diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index 6f18ba7af5..6ac3517054 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -19,6 +19,7 @@ import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.utils.DateUtil; @@ -75,6 +76,11 @@ public class Profile { } } + // Constructor from profileStore JSON + public Profile(JSONObject json) { + init(json, 100, 0); + } + public Profile(JSONObject json, int percentage, int timeshift) { init(json, percentage, timeshift); } @@ -100,8 +106,6 @@ public class Profile { units = json.getString("units").toLowerCase(); if (json.has("dia")) dia = json.getDouble("dia"); - if (json.has("dia")) - dia = json.getDouble("dia"); if (json.has("timezone")) timeZone = TimeZone.getTimeZone(json.getString("timezone")); isf = json.getJSONArray("sens"); @@ -149,7 +153,7 @@ public class Profile { return units; } - public TimeZone getTimeZone() { + TimeZone getTimeZone() { return timeZone; } @@ -162,7 +166,7 @@ public class Profile { double multiplier = getMultiplier(array); LongSparseArray sparse = new LongSparseArray<>(); - for (Integer index = 0; index < array.length(); index++) { + for (int index = 0; index < array.length(); index++) { try { final JSONObject o = array.getJSONObject(index); long tas = 0; @@ -385,15 +389,15 @@ public class Profile { return retValue; } - public double getIsf() { - return getIsfTimeFromMidnight(secondsFromMidnight()); + public double getIsfMgdl() { + return toMgdl(getIsfTimeFromMidnight(secondsFromMidnight()), units); } - public double getIsf(long time) { - return getIsfTimeFromMidnight(secondsFromMidnight(time)); + public double getIsfMgdl(long time) { + return toMgdl(getIsfTimeFromMidnight(secondsFromMidnight(time)), units); } - double getIsfTimeFromMidnight(int timeAsSeconds) { + public double getIsfTimeFromMidnight(int timeAsSeconds) { if (isf_v == null) isf_v = convertToSparseArray(isf); return getValueToTime(isf_v, timeAsSeconds); @@ -405,15 +409,15 @@ public class Profile { return getValuesList(isf_v, null, new DecimalFormat("0.0"), getUnits() + MainApp.gs(R.string.profile_per_unit)); } - public ProfileValue[] getIsfs() { + public ProfileValue[] getIsfsMgdl() { 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); + for (int index = 0; index < isf_v.size(); index++) { + int tas = (int) isf_v.keyAt(index); double value = isf_v.valueAt(index); - ret[index] = new ProfileValue(tas, value); + ret[index] = new ProfileValue(tas, toMgdl(value, units)); } return ret; } @@ -495,44 +499,44 @@ public class Profile { return ret; } - public double getTarget() { - return getTarget(secondsFromMidnight()); + public double getTargetMgdl() { + return getTargetMgdl(secondsFromMidnight()); } - protected double getTarget(int timeAsSeconds) { - return (getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds)) / 2; + public double getTargetMgdl(int timeAsSeconds) { + return toMgdl((getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds)) / 2, units); } - public double getTargetLow() { - return getTargetLowTimeFromMidnight(secondsFromMidnight()); + public double getTargetLowMgdl() { + return toMgdl(getTargetLowTimeFromMidnight(secondsFromMidnight()), units); } - public double getTargetLow(long time) { - return getTargetLowTimeFromMidnight(secondsFromMidnight(time)); + public double getTargetLowMgdl(long time) { + return toMgdl(getTargetLowTimeFromMidnight(secondsFromMidnight(time)), units); } - public double getTargetLowTimeFromMidnight(int timeAsSeconds) { + double getTargetLowTimeFromMidnight(int timeAsSeconds) { if (targetLow_v == null) targetLow_v = convertToSparseArray(targetLow); return getValueToTime(targetLow_v, timeAsSeconds); } - public double getTargetHigh() { - return getTargetHighTimeFromMidnight(secondsFromMidnight()); + public double getTargetHighMgdl() { + return toMgdl(getTargetHighTimeFromMidnight(secondsFromMidnight()), units); } - public double getTargetHigh(long time) { - return getTargetHighTimeFromMidnight(secondsFromMidnight(time)); + public double getTargetHighMgdl(long time) { + return toMgdl(getTargetHighTimeFromMidnight(secondsFromMidnight(time)), units); } - public double getTargetHighTimeFromMidnight(int timeAsSeconds) { + double getTargetHighTimeFromMidnight(int timeAsSeconds) { if (targetHigh_v == null) targetHigh_v = convertToSparseArray(targetHigh); return getValueToTime(targetHigh_v, timeAsSeconds); } public class TargetValue { - public TargetValue(int timeAsSeconds, double low, double high) { + TargetValue(int timeAsSeconds, double low, double high) { this.timeAsSeconds = timeAsSeconds; this.low = low; this.high = high; @@ -559,17 +563,17 @@ public class Profile { return ret; } - public ProfileValue[] getSingleTargets() { + public ProfileValue[] getSingleTargetsMgdl() { 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); + for (int index = 0; index < targetLow_v.size(); index++) { + int tas = (int) targetLow_v.keyAt(index); double target = (targetLow_v.valueAt(index) + targetHigh_v.valueAt(index)) / 2; - ret[index] = new ProfileValue(tas, target); + ret[index] = new ProfileValue(tas, toMgdl(target, units)); } return ret; } @@ -585,7 +589,7 @@ public class Profile { public double getMaxDailyBasal() { double max = 0d; for (int hour = 0; hour < 24; hour++) { - double value = getBasalTimeFromMidnight((Integer) (hour * 60 * 60)); + double value = getBasalTimeFromMidnight(hour * 60 * 60); if (value > max) max = value; } return max; @@ -617,22 +621,39 @@ public class Profile { else return value * Constants.MGDL_TO_MMOLL; } - public static double toUnits(Double valueInMgdl, Double valueInMmol, String units) { + public static double fromMmolToUnits(double value, String units) { + if (units.equals(Constants.MMOL)) return value; + else return value * Constants.MMOLL_TO_MGDL; + } + + public static double toUnits(double valueInMgdl, double valueInMmol, String units) { if (units.equals(Constants.MGDL)) return valueInMgdl; else return valueInMmol; } - public static String toUnitsString(Double valueInMgdl, Double valueInMmol, String units) { + public static String toUnitsString(double valueInMgdl, double valueInMmol, String units) { if (units.equals(Constants.MGDL)) return DecimalFormatter.to0Decimal(valueInMgdl); else return DecimalFormatter.to1Decimal(valueInMmol); } - public static String toSignedUnitsString(Double valueInMgdl, Double valueInMmol, String units) { + public static String toSignedUnitsString(double valueInMgdl, double valueInMmol, String units) { if (units.equals(Constants.MGDL)) return (valueInMgdl > 0 ? "+" : "") + DecimalFormatter.to0Decimal(valueInMgdl); else return (valueInMmol > 0 ? "+" : "") + DecimalFormatter.to1Decimal(valueInMmol); } + public static double toCurrentUnits(double anyBg) { + if (anyBg < 32) return fromMmolToUnits(anyBg, ProfileFunctions.getSystemUnits()); + else return fromMgdlToUnits(anyBg, ProfileFunctions.getSystemUnits()); + } + + public static String toCurrentUnitsString(double anyBg) { + if (anyBg < 32) + return toUnitsString(anyBg * Constants.MMOLL_TO_MGDL, anyBg, ProfileFunctions.getSystemUnits()); + else + return toUnitsString(anyBg, anyBg * Constants.MGDL_TO_MMOLL, ProfileFunctions.getSystemUnits()); + } + // targets are stored in mg/dl but profile vary public static String toTargetRangeString(double low, double high, String sourceUnits, String units) { double lowMgdl = toMgdl(low, sourceUnits); @@ -670,4 +691,112 @@ public class Profile { public int getTimeshift() { return timeshift; } + + public Profile convertToNonCustomizedProfile() { + JSONObject o = new JSONObject(); + try { + o.put("units", units); + o.put("dia", dia); + o.put("timezone", timeZone.getID()); + // SENS + JSONArray sens = new JSONArray(); + double lastValue = -1d; + for (int i = 0; i < 24; i++) { + int timeAsSeconds = i * 60 * 60; + double value = getIsfTimeFromMidnight(timeAsSeconds); + if (value != lastValue) { + JSONObject item = new JSONObject(); + String time; + DecimalFormat df = new DecimalFormat("00"); + time = df.format(i) + ":00"; + item.put("time", time); + item.put("timeAsSeconds", timeAsSeconds); + item.put("value", value); + lastValue = value; + sens.put(item); + } + } + o.put("sens", sens); + // CARBRATIO + JSONArray carbratio = new JSONArray(); + lastValue = -1d; + for (int i = 0; i < 24; i++) { + int timeAsSeconds = i * 60 * 60; + double value = getIcTimeFromMidnight(timeAsSeconds); + if (value != lastValue) { + JSONObject item = new JSONObject(); + String time; + DecimalFormat df = new DecimalFormat("00"); + time = df.format(i) + ":00"; + item.put("time", time); + item.put("timeAsSeconds", timeAsSeconds); + item.put("value", value); + lastValue = value; + carbratio.put(item); + } + } + o.put("carbratio", carbratio); + // BASAL + JSONArray basal = new JSONArray(); + lastValue = -1d; + for (int i = 0; i < 24; i++) { + int timeAsSeconds = i * 60 * 60; + double value = getBasalTimeFromMidnight(timeAsSeconds); + if (value != lastValue) { + JSONObject item = new JSONObject(); + String time; + DecimalFormat df = new DecimalFormat("00"); + time = df.format(i) + ":00"; + item.put("time", time); + item.put("timeAsSeconds", timeAsSeconds); + item.put("value", value); + lastValue = value; + basal.put(item); + } + } + o.put("basal", basal); + // TARGET_LOW + JSONArray target_low = new JSONArray(); + lastValue = -1d; + for (int i = 0; i < 24; i++) { + int timeAsSeconds = i * 60 * 60; + double value = getTargetLowTimeFromMidnight(timeAsSeconds); + if (value != lastValue) { + JSONObject item = new JSONObject(); + String time; + DecimalFormat df = new DecimalFormat("00"); + time = df.format(i) + ":00"; + item.put("time", time); + item.put("timeAsSeconds", timeAsSeconds); + item.put("value", value); + lastValue = value; + target_low.put(item); + } + } + o.put("target_low", target_low); + // TARGET_HIGH + JSONArray target_high = new JSONArray(); + lastValue = -1d; + for (int i = 0; i < 24; i++) { + int timeAsSeconds = i * 60 * 60; + double value = getTargetHighTimeFromMidnight(timeAsSeconds); + if (value != lastValue) { + JSONObject item = new JSONObject(); + String time; + DecimalFormat df = new DecimalFormat("00"); + time = df.format(i) + ":00"; + item.put("time", time); + item.put("timeAsSeconds", timeAsSeconds); + item.put("value", value); + lastValue = value; + target_high.put(item); + } + } + o.put("target_high", target_high); + + } catch (JSONException e) { + log.error("Unhandled exception" + e); + } + return new Profile(o); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java b/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java deleted file mode 100644 index a0b54ec0bb..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java +++ /dev/null @@ -1,118 +0,0 @@ -package info.nightscout.androidaps.data; - -import androidx.annotation.Nullable; -import androidx.collection.ArrayMap; - -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Iterator; - -import info.nightscout.androidaps.Constants; - -/** - * Created by mike on 01.06.2017. - */ - -public class ProfileStore { - private static Logger log = LoggerFactory.getLogger(ProfileStore.class); - private JSONObject json = null; - private String units = Constants.MGDL; - - ArrayMap cachedObjects = new ArrayMap<>(); - - public ProfileStore(JSONObject json) { - this.json = json; - getDefaultProfile(); // initialize units - } - - public JSONObject getData() { - return json; - } - - @Nullable - public Profile getDefaultProfile() { - Profile profile = null; - try { - String defaultProfileName = json.getString("defaultProfile"); - JSONObject store = json.getJSONObject("store"); - if (store.has(defaultProfileName)) { - profile = cachedObjects.get(defaultProfileName); - if (profile == null) { - if (store.has("units")) - units = store.getString("units"); - profile = new Profile(store.getJSONObject(defaultProfileName), units); - units = profile.getUnits(); - cachedObjects.put(defaultProfileName, profile); - } - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return profile; - } - - @Nullable - public String getDefaultProfileName() { - String defaultProfileName = null; - try { - defaultProfileName = json.getString("defaultProfile"); - JSONObject store = json.getJSONObject("store"); - if (store.has(defaultProfileName)) { - return defaultProfileName; - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return defaultProfileName; - } - - public String getUnits() { - return units; - } - - @Nullable - public Profile getSpecificProfile(String profileName) { - Profile profile = null; - try { - JSONObject store = json.getJSONObject("store"); - if (store.has(profileName)) { - profile = cachedObjects.get(profileName); - if (profile == null) { - if (store.has("units")) - units = store.getString("units"); - profile = new Profile(store.getJSONObject(profileName), units); - units = profile.getUnits(); - cachedObjects.put(profileName, profile); - } - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return profile; - } - - public ArrayList getProfileList() { - ArrayList ret = new ArrayList(); - - JSONObject store; - try { - store = json.getJSONObject("store"); - Iterator keys = store.keys(); - - while (keys.hasNext()) { - String profileName = (String) keys.next(); - ret.add(profileName); - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - - return ret; - } - - -} diff --git a/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.kt b/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.kt new file mode 100644 index 0000000000..9415a25f2c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.kt @@ -0,0 +1,60 @@ +package info.nightscout.androidaps.data + +import androidx.collection.ArrayMap +import info.nightscout.androidaps.utils.JsonHelper +import org.json.JSONException +import org.json.JSONObject +import org.slf4j.LoggerFactory +import java.util.* + +class ProfileStore(val data: JSONObject) { + private val log = LoggerFactory.getLogger(ProfileStore::class.java) + + private val cachedObjects = ArrayMap() + + private fun getStore(): JSONObject? { + try { + if (data.has("store")) return data.getJSONObject("store") + } catch (e: JSONException) { + log.error("Unhandled exception", e) + } + return null + } + + fun getDefaultProfile(): Profile? = getDefaultProfileName()?.let { getSpecificProfile(it) } + + fun getDefaultProfileName(): String? { + val defaultProfileName = data.getString("defaultProfile") + return getStore()?.has(defaultProfileName)?.let { defaultProfileName } + } + + fun getProfileList(): ArrayList { + val ret = ArrayList() + getStore()?.keys()?.let { keys -> + while (keys.hasNext()) { + val profileName = keys.next() as String + ret.add(profileName) + } + } + return ret + } + + fun getSpecificProfile(profileName: String): Profile? { + var profile: Profile? = null + getStore()?.let { store -> + if (store.has(profileName)) { + profile = cachedObjects[profileName] + if (profile == null) { + JsonHelper.safeGetJSONObject(store, profileName, null)?.let { profileObject -> + // take units from profile and if N/A from store + JsonHelper.safeGetStringAllowNull(profileObject, "units", JsonHelper.safeGetString(data, "units"))?.let { units -> + profile = Profile(profileObject, units) + cachedObjects[profileName] = profile + } + } + } + } + } + return profile + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java b/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java index 062f6ae391..66123e1ce9 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java +++ b/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java @@ -11,6 +11,7 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.interfaces.TreatmentsInterface; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; @@ -74,7 +75,7 @@ public class QuickWizardEntry { //BG double bg = 0; if (lastBG != null && useBG() == YES) { - bg = lastBG.valueToUnits(profile.getUnits()); + bg = lastBG.valueToUnits(ProfileFunctions.getSystemUnits()); } // COB @@ -124,7 +125,8 @@ public class QuickWizardEntry { trend = true; } - return new BolusWizard(profile, profileName, tempTarget, carbs(), cob, bg, 0d, 100, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard"); + double percentage = SP.getDouble(R.string.key_boluswizard_percentage, 100.0); + return new BolusWizard(profile, profileName, tempTarget, carbs(), cob, bg, 0d, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard"); } public String buttonText() { diff --git a/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt b/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt new file mode 100644 index 0000000000..b121dbee02 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt @@ -0,0 +1,145 @@ +package info.nightscout.androidaps.data.defaultProfile + +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.utils.Round +import org.json.JSONArray +import org.json.JSONObject +import java.util.* + + +class DefaultProfile { + var oneToFive: TreeMap> = TreeMap() + var sixToEleven: TreeMap> = TreeMap() + var twelveToSeventeen: TreeMap> = TreeMap() + var eighteenToTwentyfor: TreeMap> = TreeMap() + + fun profile(age: Double, tdd: Double, weight: Double, units: String): Profile { + val profile = JSONObject() + if (age >= 1 && age < 6) { + val _tdd = if (tdd == 0.0) 0.6 * weight else tdd + closest(oneToFive, _tdd * 0.3)?.let { array -> profile.put("basal", arrayToJson(array)) } + val ic = Round.roundTo(250.0 / _tdd, 1.0) + profile.put("carbratio", singleValueArray(ic, arrayOf( 0.0, -4.0, -1.0, -2.0, -4.0, 0.0, -4.0))) + val isf = Round.roundTo(200.0 / _tdd, 0.1) + profile.put("sens", singleValueArray(isf, arrayOf( 0.0, -2.0, -0.0, -0.0, -2.0, 0.0, -2.0))) + } else if (age >= 6 && age < 12) { + val _tdd = if (tdd == 0.0) 0.8 * weight else tdd + closest(sixToEleven, _tdd * 0.4)?.let { array -> profile.put("basal", arrayToJson(array)) } + val ic = Round.roundTo(375.0 / _tdd, 1.0) + profile.put("carbratio", singleValueArray(ic, arrayOf( 0.0, -3.0, 0.0, -1.0, -3.0, 0.0, -2.0))) + val isf = Round.roundTo(170.0 / _tdd, 0.1) + profile.put("sens", singleValueArray(isf, arrayOf( 0.0, -1.0, -0.0, -0.0, -1.0, 0.0, -1.0))) + } else if (age >= 12 && age < 17) { + val _tdd = if (tdd == 0.0) 1.0 * weight else tdd + closest(twelveToSeventeen, _tdd * 0.5)?.let { array -> profile.put("basal", arrayToJson(array)) } + val ic = Round.roundTo(500.0 / _tdd, 1.0) + profile.put("carbratio", singleValueArray(ic, arrayOf( 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, -1.0))) + val isf = Round.roundTo(100.0 / _tdd, 0.1) + profile.put("sens", singleValueArray(isf, arrayOf( 0.2, 0.0, 0.2, 0.2, 0.0, 0.2, 0.2))) + } else if (age >= 18) { + + } + profile.put("dia", 5.0) + profile.put("carbs_hr", 20) // not used + profile.put("delay", 5.0) // not used + profile.put("timezone", TimeZone.getDefault().getID()) + profile.put("target_high", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units)))) + profile.put("target_low", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units)))) + return Profile(profile, units) + } + + init { + oneToFive[1.00] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050) + oneToFive[1.13] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050) + oneToFive[1.25] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.050, 0.050) + oneToFive[1.38] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.050, 0.050) + oneToFive[1.50] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.075, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.050, 0.050, 0.050, 0.075, 0.100, 0.100, 0.050, 0.050) + oneToFive[1.75] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.050, 0.050, 0.050, 0.060, 0.060, 0.075, 0.075, 0.050, 0.050, 0.050, 0.100, 0.125, 0.100, 0.050, 0.050) + oneToFive[2.00] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.075, 0.050, 0.050, 0.065, 0.065, 0.075, 0.075, 0.050, 0.050, 0.050, 0.100, 0.125, 0.100, 0.050, 0.050) + oneToFive[2.25] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.100, 0.100, 0.075, 0.060, 0.060, 0.070, 0.070, 0.100, 0.100, 0.050, 0.050, 0.050, 0.125, 0.150, 0.125, 0.065, 0.050) + oneToFive[2.50] = arrayOf(0.050, 0.050, 0.050, 0.050, 0.075, 0.075, 0.100, 0.125, 0.125, 0.100, 0.065, 0.065, 0.075, 0.075, 0.125, 0.125, 0.060, 0.060, 0.060, 0.150, 0.150, 0.150, 0.070, 0.060) + oneToFive[2.75] = arrayOf(0.075, 0.075, 0.075, 0.100, 0.100, 0.100, 0.125, 0.150, 0.125, 0.100, 0.070, 0.070, 0.080, 0.080, 0.150, 0.150, 0.070, 0.070, 0.070, 0.175, 0.175, 0.175, 0.080, 0.070) + oneToFive[3.25] = arrayOf(0.100, 0.100, 0.100, 0.125, 0.125, 0.125, 0.150, 0.150, 0.150, 0.100, 0.080, 0.080, 0.100, 0.100, 0.175, 0.175, 0.075, 0.075, 0.075, 0.200, 0.200, 0.200, 0.090, 0.080) + oneToFive[3.75] = arrayOf(0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.175, 0.175, 0.175, 0.100, 0.085, 0.085, 0.110, 0.110, 0.185, 0.185, 0.080, 0.080, 0.080, 0.225, 0.225, 0.225, 0.100, 0.090) + oneToFive[4.25] = arrayOf(0.125, 0.125, 0.130, 0.140, 0.140, 0.140, 0.200, 0.200, 0.200, 0.125, 0.090, 0.090, 0.120, 0.120, 0.200, 0.200, 0.100, 0.100, 0.100, 0.250, 0.250, 0.250, 0.125, 0.100) + oneToFive[4.75] = arrayOf(0.125, 0.130, 0.135, 0.150, 0.150, 0.150, 0.200, 0.225, 0.200, 0.125, 0.100, 0.100, 0.125, 0.125, 0.250, 0.200, 0.110, 0.125, 0.125, 0.275, 0.275, 0.275, 0.130, 0.125) + oneToFive[5.25] = arrayOf(0.150, 0.150, 0.150, 0.170, 0.170, 0.170, 0.225, 0.225, 0.225, 0.130, 0.125, 0.125, 0.140, 0.140, 0.250, 0.250, 0.150, 0.150, 0.150, 0.300, 0.300, 0.300, 0.150, 0.150) + oneToFive[6.00] = arrayOf(0.170, 0.170, 0.175, 0.200, 0.200, 0.200, 0.250, 0.250, 0.250, 0.150, 0.125, 0.125, 0.150, 0.150, 0.275, 0.275, 0.170, 0.150, 0.150, 0.350, 0.350, 0.350, 0.175, 0.150) + oneToFive[6.75] = arrayOf(0.200, 0.200, 0.200, 0.225, 0.225, 0.225, 0.275, 0.275, 0.275, 0.200, 0.130, 0.130, 0.175, 0.175, 0.300, 0.300, 0.170, 0.175, 0.175, 0.375, 0.375, 0.375, 0.200, 0.175) + oneToFive[7.50] = arrayOf(0.225, 0.230, 0.235, 0.250, 0.250, 0.250, 0.300, 0.300, 0.300, 0.250, 0.150, 0.150, 0.200, 0.200, 0.325, 0.325, 0.200, 0.200, 0.200, 0.400, 0.450, 0.400, 0.350, 0.200) + + sixToEleven[5.26] = arrayOf(0.18, 0.18, 0.18, 0.20, 0.20, 0.23, 0.25, 0.25, 0.25, 0.18, 0.15, 0.13, 0.15, 0.15, 0.25, 0.25, 0.20, 0.15, 0.18, 0.25, 0.25, 0.25, 0.23, 0.20) + sixToEleven[5.61] = arrayOf(0.18, 0.20, 0.20, 0.23, 0.23, 0.25, 0.28, 0.28, 0.25, 0.20, 0.15, 0.13, 0.15, 0.18, 0.25, 0.25, 0.20, 0.15, 0.18, 0.28, 0.25, 0.25, 0.23, 0.20) + sixToEleven[5.93] = arrayOf(0.20, 0.20, 0.23, 0.25, 0.25, 0.25, 0.30, 0.30, 0.30, 0.25, 0.15, 0.15, 0.18, 0.18, 0.28, 0.28, 0.20, 0.20, 0.20, 0.28, 0.28, 0.28, 0.25, 0.23) + sixToEleven[6.26] = arrayOf(0.20, 0.23, 0.23, 0.25, 0.25, 0.28, 0.33, 0.30, 0.30, 0.25, 0.18, 0.15, 0.18, 0.18, 0.28, 0.28, 0.23, 0.20, 0.20, 0.28, 0.30, 0.28, 0.25, 0.23) + sixToEleven[6.60] = arrayOf(0.23, 0.23, 0.25, 0.25, 0.25, 0.28, 0.33, 0.33, 0.33, 0.28, 0.18, 0.15, 0.18, 0.18, 0.30, 0.28, 0.23, 0.23, 0.20, 0.30, 0.30, 0.30, 0.25, 0.25) + sixToEleven[7.26] = arrayOf(0.23, 0.25, 0.28, 0.28, 0.30, 0.33, 0.38, 0.35, 0.35, 0.30, 0.18, 0.18, 0.18, 0.19, 0.33, 0.30, 0.25, 0.23, 0.23, 0.33, 0.33, 0.33, 0.30, 0.25) + sixToEleven[7.92] = arrayOf(0.25, 0.25, 0.28, 0.30, 0.33, 0.35, 0.38, 0.38, 0.38, 0.35, 0.20, 0.20, 0.23, 0.25, 0.35, 0.33, 0.30, 0.25, 0.25, 0.35, 0.35, 0.35, 0.33, 0.28) + sixToEleven[8.57] = arrayOf(0.28, 0.28, 0.30, 0.30, 0.33, 0.38, 0.40, 0.43, 0.40, 0.38, 0.25, 0.25, 0.25, 0.28, 0.38, 0.38, 0.33, 0.28, 0.28, 0.38, 0.40, 0.38, 0.35, 0.30) + sixToEleven[9.24] = arrayOf(0.30, 0.33, 0.35, 0.38, 0.40, 0.40, 0.43, 0.45, 0.43, 0.40, 0.30, 0.25, 0.28, 0.28, 0.38, 0.40, 0.33, 0.30, 0.30, 0.40, 0.43, 0.40, 0.38, 0.35) + sixToEleven[9.89] = arrayOf(0.35, 0.35, 0.38, 0.40, 0.40, 0.43, 0.45, 0.48, 0.45, 0.40, 0.30, 0.25, 0.28, 0.30, 0.40, 0.43, 0.35, 0.33, 0.33, 0.43, 0.45, 0.43, 0.40, 0.38) + sixToEleven[10.56] = arrayOf(0.38, 0.38, 0.40, 0.43, 0.45, 0.45, 0.48, 0.50, 0.48, 0.40, 0.35, 0.25, 0.30, 0.33, 0.43, 0.45, 0.35, 0.35, 0.35, 0.45, 0.48, 0.45, 0.43, 0.40) + sixToEleven[11.21] = arrayOf(0.40, 0.43, 0.43, 0.45, 0.48, 0.50, 0.53, 0.55, 0.50, 0.40, 0.35, 0.30, 0.33, 0.33, 0.45, 0.48, 0.38, 0.35, 0.37, 0.50, 0.50, 0.48, 0.45, 0.43) + sixToEleven[11.88] = arrayOf(0.43, 0.43, 0.45, 0.45, 0.48, 0.50, 0.55, 0.58, 0.50, 0.40, 0.35, 0.33, 0.33, 0.33, 0.48, 0.50, 0.40, 0.38, 0.38, 0.53, 0.53, 0.50, 0.48, 0.45) + sixToEleven[12.53] = arrayOf(0.45, 0.45, 0.48, 0.50, 0.53, 0.55, 0.60, 0.60, 0.60, 0.45, 0.40, 0.35, 0.35, 0.38, 0.50, 0.53, 0.40, 0.38, 0.38, 0.55, 0.58, 0.55, 0.50, 0.48) + sixToEleven[13.19] = arrayOf(0.48, 0.48, 0.50, 0.55, 0.58, 0.60, 0.65, 0.65, 0.65, 0.50, 0.45, 0.36, 0.38, 0.40, 0.55, 0.55, 0.45, 0.40, 0.40, 0.60, 0.60, 0.58, 0.55, 0.50) + sixToEleven[14.18] = arrayOf(0.53, 0.53, 0.55, 0.60, 0.65, 0.68, 0.70, 0.70, 0.68, 0.60, 0.55, 0.40, 0.40, 0.45, 0.60, 0.60, 0.50, 0.45, 0.45, 0.63, 0.65, 0.63, 0.60, 0.60) + sixToEleven[15.17] = arrayOf(0.55, 0.58, 0.60, 0.65, 0.70, 0.70, 0.75, 0.75, 0.70, 0.65, 0.60, 0.42, 0.42, 0.45, 0.65, 0.65, 0.60, 0.50, 0.50, 0.68, 0.68, 0.65, 0.63, 0.63) + sixToEleven[16.50] = arrayOf(0.60, 0.63, 0.65, 0.70, 0.70, 0.70, 0.80, 0.80, 0.80, 0.70, 0.60, 0.45, 0.45, 0.50, 0.65, 0.70, 0.60, 0.55, 0.55, 0.75, 0.75, 0.70, 0.65, 0.65) + + twelveToSeventeen[10.70] = arrayOf(0.30, 0.30, 0.30, 0.30, 0.40, 0.40, 0.60, 0.60, 0.60, 0.40, 0.35, 0.30, 0.30, 0.35, 0.45, 0.50, 0.40, 0.30, 0.30, 0.40, 0.50, 0.40, 0.40, 0.30) + twelveToSeventeen[11.10] = arrayOf(0.30, 0.30, 0.30, 0.35, 0.40, 0.45, 0.60, 0.60, 0.60, 0.40, 0.40, 0.30, 0.35, 0.40, 0.50, 0.50, 0.30, 0.30, 0.30, 0.50, 0.50, 0.50, 0.30, 0.30) + twelveToSeventeen[11.60] = arrayOf(0.30, 0.30, 0.35, 0.45, 0.45, 0.50, 0.65, 0.65, 0.65, 0.45, 0.40, 0.40, 0.40, 0.40, 0.50, 0.55, 0.55, 0.45, 0.45, 0.50, 0.50, 0.50, 0.40, 0.40) + twelveToSeventeen[13.00] = arrayOf(0.40, 0.40, 0.40, 0.50, 0.55, 0.60, 0.70, 0.70, 0.70, 0.60, 0.50, 0.40, 0.40, 0.40, 0.50, 0.60, 0.60, 0.50, 0.50, 0.60, 0.60, 0.60, 0.40, 0.30) + twelveToSeventeen[15.60] = arrayOf(0.45, 0.50, 0.50, 0.60, 0.65, 0.70, 0.80, 0.80, 0.80, 0.70, 0.60, 0.60, 0.50, 0.50, 0.60, 0.70, 0.70, 0.60, 0.60, 0.60, 0.70, 0.70, 0.50, 0.50) + twelveToSeventeen[17.00] = arrayOf(0.50, 0.55, 0.60, 0.70, 0.75, 0.80, 1.00, 1.00, 1.00, 0.80, 0.70, 0.60, 0.60, 0.60, 0.70, 0.80, 0.80, 0.65, 0.65, 0.65, 0.70, 0.70, 0.60, 0.60) + twelveToSeventeen[18.00] = arrayOf(0.60, 0.65, 0.70, 0.80, 0.85, 0.90, 1.10, 1.10, 1.10, 0.90, 0.80, 0.60, 0.60, 0.60, 0.70, 0.80, 0.80, 0.70, 0.65, 0.70, 0.75, 0.70, 0.60, 0.60) + twelveToSeventeen[20.20] = arrayOf(0.70, 0.75, 0.80, 0.90, 0.95, 1.00, 1.10, 1.10, 1.10, 0.90, 0.80, 0.70, 0.70, 0.70, 0.80, 0.90, 0.90, 0.75, 0.75, 0.75, 0.80, 0.80, 0.70, 0.70) + twelveToSeventeen[21.60] = arrayOf(0.75, 0.80, 0.90, 0.90, 1.00, 1.00, 1.20, 1.20, 1.20, 0.90, 0.80, 0.70, 0.70, 0.70, 0.90, 1.00, 1.00, 0.80, 0.80, 0.80, 0.80, 0.80, 0.70, 0.70) + twelveToSeventeen[23.80] = arrayOf(0.75, 0.80, 0.90, 1.00, 1.10, 1.10, 1.20, 1.20, 1.20, 1.00, 0.90, 0.80, 0.80, 0.80, 0.90, 1.10, 1.10, 0.90, 0.90, 0.90, 1.00, 1.00, 0.80, 0.80) + twelveToSeventeen[26.10] = arrayOf(0.80, 0.80, 0.90, 1.00, 1.20, 1.20, 1.30, 1.30, 1.30, 1.10, 1.00, 0.90, 0.90, 0.90, 1.00, 1.20, 1.10, 0.90, 0.90, 1.00, 1.00, 1.00, 0.90, 0.90) + twelveToSeventeen[28.00] = arrayOf(0.90, 0.90, 1.00, 1.10, 1.10, 1.20, 1.30, 1.30, 1.30, 1.20, 1.00, 1.00, 1.00, 1.00, 1.20, 1.20, 1.20, 1.00, 1.00, 1.10, 1.10, 1.10, 0.90, 0.90) + twelveToSeventeen[30.10] = arrayOf(1.00, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.50, 1.50, 1.30, 1.20, 1.00, 1.00, 1.00, 1.30, 1.40, 1.40, 1.00, 1.00, 1.15, 1.15, 1.10, 1.00, 1.00) + twelveToSeventeen[32.60] = arrayOf(1.10, 1.10, 1.20, 1.20, 1.40, 1.50, 1.50, 1.50, 1.50, 1.30, 1.20, 1.10, 1.10, 1.10, 1.40, 1.50, 1.40, 1.10, 1.10, 1.20, 1.20, 1.20, 1.10, 1.10) + twelveToSeventeen[35.20] = arrayOf(1.20, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.70, 1.50, 1.40, 1.20, 1.10, 1.10, 1.10, 1.40, 1.50, 1.60, 1.40, 1.20, 1.20, 1.30, 1.30, 1.20, 1.20) + twelveToSeventeen[39.00] = arrayOf(1.30, 1.30, 1.40, 1.60, 1.60, 1.60, 1.90, 1.90, 1.90, 1.50, 1.30, 1.20, 1.20, 1.30, 1.50, 1.60, 1.70, 1.80, 1.50, 1.50, 1.60, 1.60, 1.30, 1.30) + twelveToSeventeen[42.80] = arrayOf(1.40, 1.40, 1.50, 1.70, 1.80, 1.80, 2.00, 2.00, 2.00, 1.80, 1.80, 1.50, 1.50, 1.50, 1.60, 1.70, 1.80, 1.90, 1.60, 1.60, 1.70, 1.70, 1.50, 1.50) + twelveToSeventeen[47.30] = arrayOf(1.50, 1.50, 1.70, 1.70, 2.00, 2.00, 2.20, 2.30, 2.20, 2.00, 1.80, 1.60, 1.60, 1.60, 1.80, 2.00, 2.10, 1.90, 1.80, 1.80, 2.00, 2.00, 1.60, 1.60) + } + + private fun closest(map: TreeMap>, key: Double): Array? { + val low = map.floorEntry(key) + val high = map.ceilingEntry(key) + var res: Array? = null + if (low != null && high != null) { + res = if (Math.abs(key - low.key) < Math.abs(key - high.key)) + low.value + else + high.value + } else if (low != null || high != null) { + res = if (low != null) low.value else high.value + } + return res + } + + fun arrayToJson(b: Array): JSONArray { + val basals = JSONArray() + for (i in 0..23) { + val time = String.format(Locale.ENGLISH, "%02d:00", i) + basals.put(JSONObject().put("time", time).put("value", b[i].toString())) + } + return basals + } + + fun singleValueArray(value: Double, sample: Array): JSONArray { + val array = JSONArray() + array.put(JSONObject().put("time", "00:00").put("value", value + sample[0])) + array.put(JSONObject().put("time", "06:00").put("value", value + sample[1])) + array.put(JSONObject().put("time", "09:00").put("value", value + sample[2])) + array.put(JSONObject().put("time", "11:00").put("value", value + sample[3])) + array.put(JSONObject().put("time", "14:00").put("value", value + sample[4])) + array.put(JSONObject().put("time", "16:00").put("value", value + sample[5])) + array.put(JSONObject().put("time", "19:00").put("value", value + sample[6])) + return array + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java index 8b206e2c78..abac9e08c0 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java +++ b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java @@ -7,6 +7,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; +import java.util.List; import java.util.Objects; import info.nightscout.androidaps.Constants; @@ -19,10 +20,11 @@ import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries; import info.nightscout.androidaps.utils.DecimalFormatter; +import info.nightscout.androidaps.utils.T; @DatabaseTable(tableName = DatabaseHelper.DATABASE_BGREADINGS) public class BgReading implements DataPointWithLabelInterface { - private static Logger log = LoggerFactory.getLogger(L.DATABASE); + private static Logger log = LoggerFactory.getLogger(L.GLUCOSE); @DatabaseField(id = true) public long date; @@ -73,9 +75,10 @@ public class BgReading implements DataPointWithLabelInterface { public String directionToSymbol() { String symbol = ""; - if (direction == null) { - symbol = "??"; - } else if (direction.compareTo("DoubleDown") == 0) { + if (direction == null) + direction = calculateDirection(); + + if (direction.compareTo("DoubleDown") == 0) { symbol = "\u21ca"; } else if (direction.compareTo("SingleDown") == 0) { symbol = "\u2193"; @@ -95,18 +98,13 @@ public class BgReading implements DataPointWithLabelInterface { return symbol; } - public static boolean isSlopeNameInvalid(String direction) { - if (direction.compareTo("NOT_COMPUTABLE") == 0 || + private static boolean isSlopeNameInvalid(String direction) { + return direction.compareTo("NOT_COMPUTABLE") == 0 || direction.compareTo("NOT COMPUTABLE") == 0 || direction.compareTo("OUT_OF_RANGE") == 0 || direction.compareTo("OUT OF RANGE") == 0 || direction.compareTo("NONE") == 0 || - direction.compareTo("NotComputable") == 0 - ) { - return true; - } else { - return false; - } + direction.compareTo("NotComputable") == 0; } @@ -123,7 +121,8 @@ public class BgReading implements DataPointWithLabelInterface { public boolean isDataChanging(BgReading other) { if (date != other.date) { - log.error("Comparing different"); + if (L.isEnabled(L.GLUCOSE)) + log.error("Comparing different"); return false; } if (value != other.value) @@ -133,7 +132,8 @@ public class BgReading implements DataPointWithLabelInterface { public boolean isEqual(BgReading other) { if (date != other.date) { - log.error("Comparing different"); + if (L.isEnabled(L.GLUCOSE)) + log.error("Comparing different"); return false; } if (value != other.value) @@ -149,7 +149,8 @@ public class BgReading implements DataPointWithLabelInterface { public void copyFrom(BgReading other) { if (date != other.date) { - log.error("Copying different"); + if (L.isEnabled(L.GLUCOSE)) + log.error("Copying different"); return; } value = other.value; @@ -181,8 +182,7 @@ public class BgReading implements DataPointWithLabelInterface { @Override public double getY() { - String units = ProfileFunctions.getInstance().getProfileUnits(); - return valueToUnits(units); + return valueToUnits(ProfileFunctions.getSystemUnits()); } @Override @@ -215,9 +215,9 @@ public class BgReading implements DataPointWithLabelInterface { @Override public int getColor() { - String units = ProfileFunctions.getInstance().getProfileUnits(); - Double lowLine = OverviewPlugin.INSTANCE.determineLowLine(units); - Double highLine = OverviewPlugin.INSTANCE.determineHighLine(units); + String units = ProfileFunctions.getSystemUnits(); + Double lowLine = OverviewPlugin.INSTANCE.determineLowLine(); + Double highLine = OverviewPlugin.INSTANCE.determineHighLine(); int color = MainApp.gc(R.color.inrange); if (isPrediction()) return getPredectionColor(); @@ -246,4 +246,53 @@ public class BgReading implements DataPointWithLabelInterface { return isaCOBPrediction || isCOBPrediction || isIOBPrediction || isUAMPrediction || isZTPrediction; } + + // Copied from xDrip+ + String calculateDirection() { + // Rework to get bgreaings from internal DB and calculate on that base + + List bgReadingsList = MainApp.getDbHelper().getAllBgreadingsDataFromTime(this.date - T.mins(10).msecs(), false); + if (bgReadingsList == null || bgReadingsList.size() < 2) + return "NONE"; + BgReading current = bgReadingsList.get(1); + BgReading previous = bgReadingsList.get(0); + + if (bgReadingsList.get(1).date < bgReadingsList.get(0).date) { + current = bgReadingsList.get(0); + previous = bgReadingsList.get(1); + } + + double slope; + + // Avoid division by 0 + if (current.date == previous.date) + slope = 0; + else + slope = (previous.value - current.value) / (previous.date - current.date); + + if (L.isEnabled(L.GLUCOSE)) + log.debug("Slope is :" + slope + " delta " + (previous.value - current.value) + " date difference " + (current.date - previous.date)); + + double slope_by_minute = slope * 60000; + String arrow = "NONE"; + + if (slope_by_minute <= (-3.5)) { + arrow = "DoubleDown"; + } else if (slope_by_minute <= (-2)) { + arrow = "SingleDown"; + } else if (slope_by_minute <= (-1)) { + arrow = "FortyFiveDown"; + } else if (slope_by_minute <= (1)) { + arrow = "Flat"; + } else if (slope_by_minute <= (2)) { + arrow = "FortyFiveUp"; + } else if (slope_by_minute <= (3.5)) { + arrow = "SingleUp"; + } else if (slope_by_minute <= (40)) { + arrow = "DoubleUp"; + } + if (L.isEnabled(L.GLUCOSE)) + log.debug("Direction set to: " + arrow); + return arrow; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index 40fadff684..c2dc6f737e 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -100,8 +100,8 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval { String hours = " " + MainApp.gs(R.string.hours) + " "; if (useShortText) { - days = "d"; - hours = "h"; + days = MainApp.gs(R.string.shortday); + hours = MainApp.gs(R.string.shorthour); } return diff.get(TimeUnit.DAYS) + days + diff.get(TimeUnit.HOURS) + hours; @@ -167,7 +167,7 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval { @Override public double getY() { - String units = ProfileFunctions.getInstance().getProfileUnits(); + String units = ProfileFunctions.getSystemUnits(); if (eventType.equals(MBG)) { double mbg = 0d; try { diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index d8926fc4c1..073f21e708 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -3,6 +3,7 @@ package info.nightscout.androidaps.db; import android.content.Context; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; + import androidx.annotation.Nullable; import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; @@ -32,7 +33,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.NonOverlappingIntervals; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.events.EventCareportalEventChange; @@ -193,15 +194,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return newVersion; } - /** - * Close the database connections and clear any cached DAOs. - */ - @Override - public void close() { - super.close(); - } - - public long size(String database) { return DatabaseUtils.queryNumEntries(getReadableDatabase(), database); } @@ -558,12 +550,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { // ------------- DbRequests handling ------------------- - public void create(DbRequest dbr) { - try { + public void create(DbRequest dbr) throws SQLException { getDaoDbRequest().create(dbr); - } catch (SQLException e) { - log.error("Unhandled exception", e); - } } public int delete(DbRequest dbr) { @@ -772,8 +760,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TempTarget tempTarget = new TempTarget() .date(trJson.getLong("mills")) .duration(JsonHelper.safeGetInt(trJson, "duration")) - .low(Profile.toMgdl(trJson.getDouble("targetBottom"), units)) - .high(Profile.toMgdl(trJson.getDouble("targetTop"), units)) + .low(Profile.toMgdl(JsonHelper.safeGetDouble(trJson, "targetBottom"), units)) + .high(Profile.toMgdl(JsonHelper.safeGetDouble(trJson, "targetTop"), units)) .reason(JsonHelper.safeGetString(trJson, "reason", "")) ._id(trJson.getString("_id")) .source(Source.NIGHTSCOUT); @@ -1425,7 +1413,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", false); Where where = queryBuilder.where(); - where.eq("eventType", event); + where.eq("eventType", event).and().isNotNull("json"); queryBuilder.limit(1L); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); @@ -1445,10 +1433,10 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); - where.ge("date", mills); + where.ge("date", mills).and().isNotNull("json").and().isNotNull("eventType"); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); - preprocessOpenAPSOfflineEvents(careportalEvents); + careportalEvents = preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1462,10 +1450,10 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); - where.between("date", start, end); + where.between("date", start, end).and().isNotNull("json").and().isNotNull("eventType"); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); - preprocessOpenAPSOfflineEvents(careportalEvents); + careportalEvents = preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1473,14 +1461,16 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return new ArrayList<>(); } - public void preprocessOpenAPSOfflineEvents(List list) { - OverlappingIntervals offlineEvents = new OverlappingIntervals(); + public List preprocessOpenAPSOfflineEvents(List list) { + NonOverlappingIntervals offlineEvents = new NonOverlappingIntervals(); + List other = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { CareportalEvent event = list.get(i); - if (!event.eventType.equals(CareportalEvent.OPENAPSOFFLINE)) continue; - offlineEvents.add(event); + if (event.eventType.equals(CareportalEvent.OPENAPSOFFLINE)) offlineEvents.add(event); + else other.add(event); } - + other.addAll(offlineEvents.getList()); + return other; } public List getCareportalEventsFromTime(long mills, String type, boolean ascending) { @@ -1489,10 +1479,10 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); - where.ge("date", mills).and().eq("eventType", type); + where.ge("date", mills).and().eq("eventType", type).and().isNotNull("json"); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); - preprocessOpenAPSOfflineEvents(careportalEvents); + careportalEvents = preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1505,9 +1495,11 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { List careportalEvents; QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); + Where where = queryBuilder.where(); + where.isNotNull("json").and().isNotNull("eventType"); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); - preprocessOpenAPSOfflineEvents(careportalEvents); + careportalEvents = preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1592,15 +1584,23 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { // ---------------- ProfileSwitch handling --------------- - public List getProfileSwitchData(boolean ascending) { + public List getProfileSwitchData(long from, boolean ascending) { try { Dao daoProfileSwitch = getDaoProfileSwitch(); List profileSwitches; QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); queryBuilder.orderBy("date", ascending); queryBuilder.limit(100L); + Where where = queryBuilder.where(); + where.ge("date", from); PreparedQuery preparedQuery = queryBuilder.prepare(); profileSwitches = daoProfileSwitch.query(preparedQuery); + //add last one without duration + ProfileSwitch last = getLastProfileSwitchWithoutDuration(); + if (last != null) { + if (!profileSwitches.contains(last)) + profileSwitches.add(last); + } return profileSwitches; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1608,6 +1608,28 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return new ArrayList<>(); } + @Nullable + private ProfileSwitch getLastProfileSwitchWithoutDuration() { + try { + Dao daoProfileSwitch = getDaoProfileSwitch(); + List profileSwitches; + QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); + queryBuilder.orderBy("date", false); + queryBuilder.limit(1L); + Where where = queryBuilder.where(); + where.eq("durationInMinutes", 0); + PreparedQuery preparedQuery = queryBuilder.prepare(); + profileSwitches = daoProfileSwitch.query(preparedQuery); + if (profileSwitches.size() > 0) + return profileSwitches.get(0); + else + return null; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return null; + } + public List getProfileSwitchEventsFromTime(long mills, boolean ascending) { try { Dao daoProfileSwitch = getDaoProfileSwitch(); diff --git a/app/src/main/java/info/nightscout/androidaps/db/DbRequest.java b/app/src/main/java/info/nightscout/androidaps/db/DbRequest.java index f287e169ce..6cd0491907 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DbRequest.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DbRequest.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.utils.DateUtil; /** * Created by mike on 27.02.2016. @@ -40,66 +41,55 @@ public class DbRequest { } // dbAdd - public DbRequest(String action, String collection, String nsClientID, JSONObject data) { + public DbRequest(String action, String collection, JSONObject json) { this.action = action; this.collection = collection; - this.data = data.toString(); - this.nsClientID = nsClientID; + this.nsClientID = "" + DateUtil.now(); + try { + json.put("NSCLIENT_ID", nsClientID); + } catch (JSONException e) { + e.printStackTrace(); + } + this.data = json.toString(); this._id = ""; } // dbUpdate, dbUpdateUnset - public DbRequest(String action, String collection, String nsClientID, String _id, JSONObject data) { + public DbRequest(String action, String collection, String _id, JSONObject json) { this.action = action; this.collection = collection; - this.data = data.toString(); - this.nsClientID = nsClientID; + this.nsClientID = "" + DateUtil.now(); + try { + json.put("NSCLIENT_ID", nsClientID); + } catch (JSONException e) { + e.printStackTrace(); + } + this.data = json.toString(); this._id = _id; } // dbRemove - public DbRequest(String action, String collection, String nsClientID, String _id) { + public DbRequest(String action, String collection, + String _id) { + JSONObject json = new JSONObject(); this.action = action; this.collection = collection; - this.data = new JSONObject().toString(); - this.nsClientID = nsClientID; + this.nsClientID = "" + DateUtil.now(); + try { + json.put("NSCLIENT_ID", nsClientID); + } catch (JSONException e) { + e.printStackTrace(); + } + this.data = json.toString(); this._id = _id; } - public String hash() { - return Hashing.sha1().hashString(action + collection + _id + data.toString(), Charsets.UTF_8).toString(); - } - - public JSONObject toJSON() { - JSONObject object = new JSONObject(); - try { - object.put("action", action); - object.put("collection", collection); - object.put("data", new JSONObject(data)); - if (_id != null) object.put("_id", _id); - if (nsClientID != null) object.put("nsClientID", nsClientID); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return object; - } - - public static DbRequest fromJSON(JSONObject jsonObject) { - DbRequest result = new DbRequest(); - try { - if (jsonObject.has("action")) - result.action = jsonObject.getString("action"); - if (jsonObject.has("collection")) - result.collection = jsonObject.getString("collection"); - if (jsonObject.has("data")) - result.data = jsonObject.getJSONObject("data").toString(); - if (jsonObject.has("_id")) - result._id = jsonObject.getString("_id"); - if (jsonObject.has("nsClientID")) - result.nsClientID = jsonObject.getString("nsClientID"); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return result; + public String log() { + return + "\nnsClientID:" + nsClientID + + "\naction:" + action + + "\ncollection:" + collection + + "\ndata:" + data + + "\n_id:" + _id; } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java index 25d2c308c1..1632751d55 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java @@ -259,11 +259,11 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { double sensitivityRatio = lastAutosensResult.ratio; double normalTarget = 100; - if (exercise_mode && isTempTarget && profile.getTarget() >= normalTarget + 5) { + if (exercise_mode && isTempTarget && profile.getTargetMgdl() >= normalTarget + 5) { // w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44 // e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6 double c = half_basal_exercise_target - normalTarget; - sensitivityRatio = c / (c + profile.getTarget() - normalTarget); + sensitivityRatio = c / (c + profile.getTargetMgdl() - normalTarget); } if (realDuration > 0) { @@ -325,8 +325,8 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { } public String toStringMedium() { - return "E " + DecimalFormatter.to2Decimal(absoluteRate()) + "U/h (" - + getRealDuration() + "/" + durationInMinutes + ") "; + return DecimalFormatter.to2Decimal(absoluteRate()) + "U/h " + + getRealDuration() + "/" + durationInMinutes + "'"; } public String toStringTotal() { diff --git a/app/src/main/java/info/nightscout/androidaps/db/TDD.java b/app/src/main/java/info/nightscout/androidaps/db/TDD.java index 93a228316c..be81e36ba3 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TDD.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TDD.java @@ -6,8 +6,11 @@ import com.j256.ormlite.table.DatabaseTable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.utils.DateUtil; /** * Created by mike on 20.09.2017. @@ -56,4 +59,12 @@ public class TDD { ", total=" + total + ']'; } + + public String toText() { + return MainApp.gs(R.string.tddformat, DateUtil.dateStringShort(date), total, bolus, basal); + } + + public String toText(int days) { + return MainApp.gs(R.string.tddformat, String.format("%d ", days) + MainApp.gs(R.string.days), total, bolus, basal); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java index f2a10cf4a4..cf0eeab462 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java @@ -299,11 +299,11 @@ public class TemporaryBasal implements Interval, DbObjectBase { double sensitivityRatio = lastAutosensResult.ratio; double normalTarget = 100; - if (exercise_mode && isTempTarget && profile.getTarget() >= normalTarget + 5) { + if (exercise_mode && isTempTarget && profile.getTargetMgdl() >= normalTarget + 5) { // w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44 // e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6 double c = half_basal_exercise_target - normalTarget; - sensitivityRatio = c / (c + profile.getTarget() - normalTarget); + sensitivityRatio = c / (c + profile.getTargetMgdl() - normalTarget); } if (realDuration > 0) { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/BolusProgressDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/BolusProgressDialog.kt new file mode 100644 index 0000000000..3f629add06 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/BolusProgressDialog.kt @@ -0,0 +1,159 @@ +package info.nightscout.androidaps.dialogs + +import android.app.Activity +import android.os.Bundle +import android.os.SystemClock +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.Window +import android.view.WindowManager +import androidx.fragment.app.DialogFragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.BolusProgressHelperActivity +import info.nightscout.androidaps.events.EventPumpStatusChanged +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus.toObservable +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning +import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress +import info.nightscout.androidaps.utils.FabricPrivacy +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.dialog_bolusprogress.* +import org.slf4j.LoggerFactory + +class BolusProgressDialog : DialogFragment() { + private val log = LoggerFactory.getLogger(L.UI) + private val disposable = CompositeDisposable() + + companion object { + private val DEFAULT_STATE = MainApp.gs(R.string.waitingforpump) + @JvmField + var bolusEnded = false + @JvmField + var stopPressed = false + } + + private var running = true + private var amount = 0.0 + private var state: String? = null + private var helpActivity: BolusProgressHelperActivity? = null + + fun setInsulin(amount: Double): BolusProgressDialog { + this.amount = amount + bolusEnded = false + return this + } + + fun setHelperActivity(activity: BolusProgressHelperActivity): BolusProgressDialog { + helpActivity = activity + return this + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) + dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) + isCancelable = false + dialog?.setCanceledOnTouchOutside(false) + return inflater.inflate(R.layout.dialog_bolusprogress, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + overview_bolusprogress_title.text = String.format(MainApp.gs(R.string.overview_bolusprogress_goingtodeliver), amount) + overview_bolusprogress_stop.setOnClickListener { + if (L.isEnabled(L.UI)) log.debug("Stop bolus delivery button pressed") + stopPressed = true + overview_bolusprogress_stoppressed.visibility = View.VISIBLE + overview_bolusprogress_stop.visibility = View.INVISIBLE + ConfigBuilderPlugin.getPlugin().commandQueue.cancelAllBoluses() + } + overview_bolusprogress_progressbar.max = 100 + state = savedInstanceState?.getString("state", DEFAULT_STATE) ?: DEFAULT_STATE + overview_bolusprogress_status.text = state + stopPressed = false + } + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + } + + override fun onResume() { + super.onResume() + if (L.isEnabled(L.UI)) log.debug("onResume") + if (!ConfigBuilderPlugin.getPlugin().commandQueue.bolusInQueue()) + bolusEnded = true + + if (bolusEnded) dismiss() + else running = true + + disposable.add(toObservable(EventPumpStatusChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ overview_bolusprogress_status.text = it.getStatus() }) { FabricPrivacy.logException(it) } + ) + disposable.add(toObservable(EventDismissBolusProgressIfRunning::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ if (running) dismiss() }) { FabricPrivacy.logException(it) } + ) + disposable.add(toObservable(EventOverviewBolusProgress::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + if (L.isEnabled(L.UI)) log.debug("Status: " + it.status + " Percent: " + it.percent) + overview_bolusprogress_status.text = it.status + overview_bolusprogress_progressbar.progress = it.percent + if (it.percent == 100) { + overview_bolusprogress_stop.visibility = View.INVISIBLE + scheduleDismiss() + } + state = it.status + }) { FabricPrivacy.logException(it) } + ) + } + + override fun dismiss() { + if (L.isEnabled(L.UI)) log.debug("dismiss") + try { + super.dismiss() + } catch (e: IllegalStateException) { + // dialog not running yet. onResume will try again. Set bolusEnded to make extra + // sure onResume will catch this + bolusEnded = true + log.error("Unhandled exception", e) + } + helpActivity?.finish() + } + + override fun onPause() { + super.onPause() + if (L.isEnabled(L.UI)) log.debug("onPause") + running = false + disposable.clear() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString("state", state) + } + + private fun scheduleDismiss() { + if (L.isEnabled(L.UI)) log.debug("scheduleDismiss") + Thread(Runnable { + SystemClock.sleep(5000) + bolusEnded = true + val activity: Activity? = activity + activity?.runOnUiThread { + if (running) { + if (L.isEnabled(L.UI)) log.debug("executing") + try { + dismiss() + } catch (e: Exception) { + log.error("Unhandled exception", e) + } + } + } + }).start() + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt new file mode 100644 index 0000000000..9503988dce --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt @@ -0,0 +1,69 @@ +package info.nightscout.androidaps.dialogs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.common.base.Joiner +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.XdripCalibrations +import kotlinx.android.synthetic.main.dialog_calibration.* +import kotlinx.android.synthetic.main.okcancel.* +import java.text.DecimalFormat +import java.util.* + +class CalibrationDialog : DialogFragmentWithDate() { + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("overview_calibration_bg", overview_calibration_bg.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_calibration, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val units = ProfileFunctions.getSystemUnits() + val bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData()?.glucose + ?: 0.0, units) + if (units == Constants.MMOL) + overview_calibration_bg.setParams(savedInstanceState?.getDouble("overview_calibration_bg") + ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok) + else + overview_calibration_bg.setParams(savedInstanceState?.getDouble("overview_calibration_bg") + ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, ok) + overview_calibration_units.text = if (units == Constants.MMOL) MainApp.gs(R.string.mmol) else MainApp.gs(R.string.mgdl) + } + + override fun submit() :Boolean { + val units = ProfileFunctions.getSystemUnits() + val unitLabel = if (units == Constants.MMOL) MainApp.gs(R.string.mmol) else MainApp.gs(R.string.mgdl) + val actions: LinkedList = LinkedList() + val bg = overview_calibration_bg.value + actions.add(MainApp.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(bg) + " " + unitLabel) + if (bg > 0) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + log.debug("USER ENTRY: CALIBRATION $bg") + XdripCalibrations.confirmAndSendCalibration(bg, context) + }) + } + } else + activity?.let { activity -> + OKDialog.show(activity, MainApp.gs(R.string.overview_calibration), MainApp.gs(R.string.no_action_selected)) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt new file mode 100644 index 0000000000..1f0c6643c1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -0,0 +1,228 @@ +package info.nightscout.androidaps.dialogs + +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.common.base.Joiner +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.db.DatabaseHelper +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.db.TempTarget +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.plugins.treatments.CarbsGenerator +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.* +import kotlinx.android.synthetic.main.dialog_carbs.* +import kotlinx.android.synthetic.main.notes.* +import kotlinx.android.synthetic.main.okcancel.* +import java.text.DecimalFormat +import java.util.* +import kotlin.math.max + +class CarbsDialog : DialogFragmentWithDate() { + + companion object { + private const val FAV1_DEFAULT = 5 + private const val FAV2_DEFAULT = 10 + private const val FAV3_DEFAULT = 20 + } + + private val maxCarbs = MainApp.getConstraintChecker().maxCarbsAllowed.value().toDouble() + + private val textWatcher: TextWatcher = object : TextWatcher { + override fun afterTextChanged(s: Editable) { + validateInputs() + } + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + } + + private fun validateInputs() { + val time = overview_carbs_time.value.toInt() + if (time > 12 * 60 || time < -12 * 60) { + overview_carbs_time.value = 0.0 + ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.constraintapllied)) + } + if (overview_carbs_duration.value > 10) { + overview_carbs_duration.value = 0.0 + ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.constraintapllied)) + } + if (overview_carbs_carbs.value.toInt() > maxCarbs) { + overview_carbs_carbs.value = 0.0 + ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.carbsconstraintapplied)) + } + } + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("overview_carbs_time", overview_carbs_time.value) + savedInstanceState.putDouble("overview_carbs_duration", overview_carbs_duration.value) + savedInstanceState.putDouble("overview_carbs_carbs", overview_carbs_carbs.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_carbs, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + overview_carbs_time.setParams(savedInstanceState?.getDouble("overview_carbs_time") + ?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) + + overview_carbs_duration.setParams(savedInstanceState?.getDouble("overview_carbs_duration") + ?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, ok, textWatcher) + + overview_carbs_carbs.setParams(savedInstanceState?.getDouble("overview_carbs_carbs") + ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, ok, textWatcher) + + overview_carbs_plus1.text = toSignedString(SP.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) + overview_carbs_plus1.setOnClickListener { + overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value + + SP.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) + validateInputs() + } + + overview_carbs_plus2.text = toSignedString(SP.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) + overview_carbs_plus2.setOnClickListener { + overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value + + SP.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) + validateInputs() + } + + overview_carbs_plus3.text = toSignedString(SP.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) + overview_carbs_plus3.setOnClickListener { + overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value + + SP.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) + validateInputs() + } + + DatabaseHelper.actualBg()?.let { bgReading -> + if (bgReading.value < 72) + overview_carbs_hypo_tt.setChecked(true) + } + overview_carbs_hypo_tt.setOnClickListener { + overview_carbs_activity_tt.isChecked = false + overview_carbs_eating_soon_tt.isChecked = false + } + overview_carbs_activity_tt.setOnClickListener { + overview_carbs_hypo_tt.isChecked = false + overview_carbs_eating_soon_tt.isChecked = false + } + overview_carbs_eating_soon_tt.setOnClickListener { + overview_carbs_hypo_tt.isChecked = false + overview_carbs_activity_tt.isChecked = false + } + } + + private fun toSignedString(value: Int): String { + return if (value > 0) "+$value" else value.toString() + } + + override fun submit(): Boolean { + val carbs = overview_carbs_carbs.value.toInt() + val carbsAfterConstraints = MainApp.getConstraintChecker().applyCarbsConstraints(Constraint(carbs)).value() + val units = ProfileFunctions.getSystemUnits() + val activityTTDuration = DefaultValueHelper.determineActivityTTDuration() + val activityTT = DefaultValueHelper.determineActivityTT() + val eatingSoonTTDuration = DefaultValueHelper.determineEatingSoonTTDuration() + val eatingSoonTT = DefaultValueHelper.determineEatingSoonTT() + val hypoTTDuration = DefaultValueHelper.determineHypoTTDuration() + val hypoTT = DefaultValueHelper.determineHypoTT() + val actions: LinkedList = LinkedList() + val unitLabel = if (units == Constants.MMOL) MainApp.gs(R.string.mmol) else MainApp.gs(R.string.mgdl) + + val activitySelected = overview_carbs_activity_tt.isChecked + if (activitySelected) + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + activityTTDuration + " " + MainApp.gs(R.string.unit_minute_short) + ")") + val eatingSoonSelected = overview_carbs_eating_soon_tt.isChecked + if (eatingSoonSelected) + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + eatingSoonTTDuration + " " + MainApp.gs(R.string.unit_minute_short) + ")") + val hypoSelected = overview_carbs_hypo_tt.isChecked + if (hypoSelected) + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(hypoTT) + " " + unitLabel + " (" + hypoTTDuration + " " + MainApp.gs(R.string.unit_minute_short) + ")") + + val timeOffset = overview_carbs_time.value.toInt() + val time = eventTime + timeOffset * 1000 * 60 + if (timeOffset != 0) + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(time)) + val duration = overview_carbs_duration.value.toInt() + if (duration > 0) + actions.add(MainApp.gs(R.string.duration) + ": " + duration + MainApp.gs(R.string.shorthour)) + if (carbsAfterConstraints > 0) { + actions.add(MainApp.gs(R.string.carbs) + ": " + "" + MainApp.gs(R.string.format_carbs, carbsAfterConstraints) + "") + if (carbsAfterConstraints != carbs) + actions.add("" + MainApp.gs(R.string.carbsconstraintapplied) + "") + } + val notes = notes.text.toString() + if (notes.isNotEmpty()) + actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) + + if (eventTimeChanged) + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(eventTime)) + + if (carbsAfterConstraints > 0 || activitySelected || eatingSoonSelected || hypoSelected) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + if (activitySelected) { + log.debug("USER ENTRY: TEMPTARGET ACTIVITY $activityTT duration: $activityTTDuration") + val tempTarget = TempTarget() + .date(eventTime) + .duration(activityTTDuration) + .reason(MainApp.gs(R.string.activity)) + .source(Source.USER) + .low(Profile.toMgdl(activityTT, ProfileFunctions.getSystemUnits())) + .high(Profile.toMgdl(activityTT, ProfileFunctions.getSystemUnits())) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + } else if (eatingSoonSelected) { + log.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration") + val tempTarget = TempTarget() + .date(eventTime) + .duration(eatingSoonTTDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(eatingSoonTT, ProfileFunctions.getSystemUnits())) + .high(Profile.toMgdl(eatingSoonTT, ProfileFunctions.getSystemUnits())) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + } else if (hypoSelected) { + log.debug("USER ENTRY: TEMPTARGET HYPO $hypoTT duration: $hypoTTDuration") + val tempTarget = TempTarget() + .date(eventTime) + .duration(hypoTTDuration) + .reason(MainApp.gs(R.string.hypo)) + .source(Source.USER) + .low(Profile.toMgdl(hypoTT, ProfileFunctions.getSystemUnits())) + .high(Profile.toMgdl(hypoTT, ProfileFunctions.getSystemUnits())) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + } + if (carbsAfterConstraints > 0) { + if (duration == 0) { + log.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time") + CarbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes) + } else { + log.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time duration: $duration") + CarbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes) + NSUpload.uploadEvent(CareportalEvent.NOTE, time - 2000, MainApp.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset)) + } + } + }, null) + } + } else + activity?.let { activity -> + OKDialog.show(activity, MainApp.gs(R.string.carbs), MainApp.gs(R.string.no_action_selected)) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt new file mode 100644 index 0000000000..da4a091b1d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt @@ -0,0 +1,190 @@ +package info.nightscout.androidaps.dialogs + +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import androidx.annotation.StringRes +import com.google.common.base.Joiner +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.SP +import info.nightscout.androidaps.utils.Translator +import kotlinx.android.synthetic.main.dialog_care.* +import kotlinx.android.synthetic.main.notes.* +import kotlinx.android.synthetic.main.okcancel.* +import org.json.JSONObject +import java.text.DecimalFormat +import java.util.* + +class CareDialog : DialogFragmentWithDate() { + + enum class EventType { + BGCHECK, + SENSOR_INSERT, + BATTERY_CHANGE, + NOTE, + EXERCISE + } + + private var options: EventType = EventType.BGCHECK + @StringRes + private var event: Int = R.string.none + + fun setOptions(options: EventType, @StringRes event: Int): CareDialog { + this.options = options + this.event = event + return this + } + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("actions_care_bg", actions_care_bg.value) + savedInstanceState.putDouble("actions_care_duration", actions_care_duration.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_care, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + actions_care_icon.setImageResource(when (options) { + EventType.BGCHECK -> R.drawable.icon_cp_bgcheck + EventType.SENSOR_INSERT -> R.drawable.icon_cp_cgm_insert + EventType.BATTERY_CHANGE -> R.drawable.icon_cp_pump_battery + EventType.NOTE -> R.drawable.icon_cp_note + EventType.EXERCISE -> R.drawable.icon_cp_exercise + }) + actions_care_title.text = MainApp.gs(when (options) { + EventType.BGCHECK -> R.string.careportal_bgcheck + EventType.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert + EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange + EventType.NOTE -> R.string.careportal_note + EventType.EXERCISE -> R.string.careportal_exercise + }) + + when (options) { + EventType.BGCHECK -> { + action_care_duration_layout.visibility = View.GONE + } + EventType.SENSOR_INSERT, + EventType.BATTERY_CHANGE -> { + action_care_bg_layout.visibility = View.GONE + actions_care_bgsource.visibility = View.GONE + action_care_duration_layout.visibility = View.GONE + } + EventType.NOTE, + EventType.EXERCISE -> { + action_care_bg_layout.visibility = View.GONE + actions_care_bgsource.visibility = View.GONE + } + } + + val bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData()?.glucose + ?: 0.0, ProfileFunctions.getSystemUnits()) + val bgTextWatcher: TextWatcher = object : TextWatcher { + override fun afterTextChanged(s: Editable) {} + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + if (actions_care_sensor.isChecked) actions_care_meter.isChecked = true + } + } + + if (ProfileFunctions.getSystemUnits() == Constants.MMOL) { + actions_care_bgunits.text = MainApp.gs(R.string.mmol) + actions_care_bg.setParams(savedInstanceState?.getDouble("actions_care_bg") + ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok, bgTextWatcher) + } else { + actions_care_bgunits.text = MainApp.gs(R.string.mgdl) + actions_care_bg.setParams(savedInstanceState?.getDouble("actions_care_bg") + ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, ok, bgTextWatcher) + } + actions_care_duration.setParams(savedInstanceState?.getDouble("actions_care_duration") + ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) + if (options == EventType.NOTE) + notes_layout?.visibility = View.VISIBLE // independent to preferences + } + + override fun submit(): Boolean { + val enteredBy = SP.getString("careportal_enteredby", "") + val unitResId = if (ProfileFunctions.getSystemUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol + + val json = JSONObject() + val actions: LinkedList = LinkedList() + if (options == EventType.BGCHECK) { + val type = + when { + actions_care_meter.isChecked -> "Finger" + actions_care_sensor.isChecked -> "Sensor" + else -> "Manual" + } + actions.add(MainApp.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + Translator.translate(type)) + actions.add(MainApp.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(actions_care_bg.value) + " " + MainApp.gs(unitResId)) + json.put("glucose", actions_care_bg.value) + json.put("glucoseType", type) + } + if (options == EventType.NOTE || options == EventType.EXERCISE) { + actions.add(MainApp.gs(R.string.careportal_newnstreatment_duration_label) + ": " + MainApp.gs(R.string.format_mins, actions_care_duration.value.toInt())) + json.put("duration", actions_care_duration.value.toInt()) + } + val notes = notes.text.toString() + if (notes.isNotEmpty()) { + actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) + json.put("notes", notes) + } + eventTime -= eventTime % 1000 + + if (eventTimeChanged) + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(eventTime)) + + json.put("created_at", DateUtil.toISOString(eventTime)) + json.put("mills", eventTime) + json.put("eventType", when (options) { + EventType.BGCHECK -> CareportalEvent.BGCHECK + EventType.SENSOR_INSERT -> CareportalEvent.SENSORCHANGE + EventType.BATTERY_CHANGE -> CareportalEvent.PUMPBATTERYCHANGE + EventType.NOTE -> CareportalEvent.NOTE + EventType.EXERCISE -> CareportalEvent.EXERCISE + }) + json.put("units", ProfileFunctions.getSystemUnits()) + if (enteredBy.isNotEmpty()) + json.put("enteredBy", enteredBy) + + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(event), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + val careportalEvent = CareportalEvent() + careportalEvent.date = eventTime + careportalEvent.source = Source.USER + careportalEvent.eventType = when (options) { + EventType.BGCHECK -> CareportalEvent.BGCHECK + EventType.SENSOR_INSERT -> CareportalEvent.SENSORCHANGE + EventType.BATTERY_CHANGE -> CareportalEvent.PUMPBATTERYCHANGE + EventType.NOTE -> CareportalEvent.NOTE + EventType.EXERCISE -> CareportalEvent.EXERCISE + } + careportalEvent.json = json.toString() + log.debug("USER ENTRY: CAREPORTAL ${careportalEvent.eventType} json: ${careportalEvent.json}") + MainApp.getDbHelper().createOrUpdate(careportalEvent) + NSUpload.uploadCareportalEntryToNS(json) + }, null) + } + return true + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/DialogFragmentWithDate.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/DialogFragmentWithDate.kt new file mode 100644 index 0000000000..ecaea0bc81 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/DialogFragmentWithDate.kt @@ -0,0 +1,124 @@ +package info.nightscout.androidaps.dialogs + +import android.app.DatePickerDialog +import android.app.TimePickerDialog +import android.os.Bundle +import android.text.format.DateFormat +import android.view.View +import android.view.ViewGroup +import android.view.Window +import android.view.WindowManager +import androidx.fragment.app.DialogFragment +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.SP +import info.nightscout.androidaps.utils.toVisibility +import kotlinx.android.synthetic.main.datetime.* +import kotlinx.android.synthetic.main.notes.* +import kotlinx.android.synthetic.main.okcancel.* +import org.slf4j.LoggerFactory +import java.util.* + +abstract class DialogFragmentWithDate : DialogFragment() { + val log = LoggerFactory.getLogger(DialogFragmentWithDate::class.java) + + var eventTime = DateUtil.now() + var eventTimeChanged = false + + //one shot guards + private var okClicked: Boolean = false + + companion object { + private var seconds: Int = (Math.random() * 59.0).toInt() + } + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + } + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putLong("eventTime", eventTime) + savedInstanceState.putBoolean("eventTimeChanged", eventTimeChanged) + } + + fun onCreateViewGeneral() { + dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) + dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) + isCancelable = true + dialog?.setCanceledOnTouchOutside(false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + eventTime = savedInstanceState?.getLong("eventTime") ?: DateUtil.now() + eventTimeChanged = savedInstanceState?.getBoolean("eventTimeChanged") ?: false + overview_eventdate?.text = DateUtil.dateString(eventTime) + overview_eventtime?.text = DateUtil.timeString(eventTime) + + // create an OnDateSetListener + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> + val cal = Calendar.getInstance() + cal.timeInMillis = eventTime + cal.set(Calendar.YEAR, year) + cal.set(Calendar.MONTH, monthOfYear) + cal.set(Calendar.DAY_OF_MONTH, dayOfMonth) + eventTime = cal.timeInMillis + eventTimeChanged = true + overview_eventdate?.text = DateUtil.dateString(eventTime) + } + + overview_eventdate?.setOnClickListener { + context?.let { + val cal = Calendar.getInstance() + cal.timeInMillis = eventTime + DatePickerDialog(it, dateSetListener, + cal.get(Calendar.YEAR), + cal.get(Calendar.MONTH), + cal.get(Calendar.DAY_OF_MONTH) + ).show() + } + } + + // create an OnTimeSetListener + val timeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute -> + val cal = Calendar.getInstance() + cal.timeInMillis = eventTime + cal.set(Calendar.HOUR_OF_DAY, hour) + cal.set(Calendar.MINUTE, minute) + cal.set(Calendar.SECOND, seconds++) // randomize seconds to prevent creating record of the same time, if user choose time manually + eventTime = cal.timeInMillis + eventTimeChanged = true + overview_eventtime?.text = DateUtil.timeString(eventTime) + } + + overview_eventtime?.setOnClickListener { + context?.let { + val cal = Calendar.getInstance() + cal.timeInMillis = eventTime + TimePickerDialog(it, timeSetListener, + cal.get(Calendar.HOUR_OF_DAY), + cal.get(Calendar.MINUTE), + DateFormat.is24HourFormat(context) + ).show() + } + } + + notes_layout?.visibility = SP.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() + + ok.setOnClickListener { + synchronized(okClicked) { + if (okClicked) { + log.debug("guarding: ok already clicked") + } else { + okClicked = true + if (submit()) dismiss() + else okClicked = false + } + } + } + cancel.setOnClickListener { dismiss() } + } + + abstract fun submit(): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/ErrorDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ErrorDialog.kt similarity index 72% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/ErrorDialog.kt rename to app/src/main/java/info/nightscout/androidaps/dialogs/ErrorDialog.kt index 8ea499d4ef..c084df7bf3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/ErrorDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ErrorDialog.kt @@ -1,5 +1,4 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs - +package info.nightscout.androidaps.dialogs import android.content.Intent import android.os.Build @@ -7,11 +6,14 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.Window +import android.view.WindowManager import androidx.fragment.app.DialogFragment import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.services.AlarmSoundService -import kotlinx.android.synthetic.main.overview_error_dialog.* +import kotlinx.android.synthetic.main.dialog_error.* import org.slf4j.LoggerFactory class ErrorDialog : DialogFragment() { @@ -24,26 +26,30 @@ class ErrorDialog : DialogFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - dialog?.setTitle(title) - isCancelable = false + dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) + dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) + isCancelable = true + dialog?.setCanceledOnTouchOutside(false) savedInstanceState?.let { bundle -> bundle.getString("status")?.let { status = it } bundle.getString("title")?.let { title = it } sound = bundle.getInt("sound", R.raw.error) } - return inflater.inflate(R.layout.overview_error_dialog, container, false) + log.debug("Error dialog displayed") + return inflater.inflate(R.layout.dialog_error, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + error_title.text = title overview_error_ok.setOnClickListener { - log.debug("Error dialog ok button pressed") + log.debug("USER ENTRY: Error dialog ok button pressed") dismiss() } overview_error_mute.setOnClickListener { - log.debug("Error dialog mute button pressed") + log.debug("USER ENTRY: Error dialog mute button pressed") stopAlarm() } startAlarm() @@ -56,9 +62,13 @@ class ErrorDialog : DialogFragment() { bundle.putInt("sound", sound) } + override fun onStart() { + super.onStart() + dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + } + override fun onResume() { super.onResume() - dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) overview_error_status.text = status } @@ -81,5 +91,5 @@ class ErrorDialog : DialogFragment() { } private fun stopAlarm() = - MainApp.instance().stopService(Intent(MainApp.instance().applicationContext, AlarmSoundService::class.java)) + MainApp.instance().stopService(Intent(MainApp.instance().applicationContext, AlarmSoundService::class.java)) } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt new file mode 100644 index 0000000000..dee77051fd --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt @@ -0,0 +1,83 @@ +package info.nightscout.androidaps.dialogs + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.common.base.Joiner +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.SafeParse +import kotlinx.android.synthetic.main.dialog_extendedbolus.* +import kotlinx.android.synthetic.main.okcancel.* +import java.text.DecimalFormat +import java.util.* +import kotlin.math.abs + +class ExtendedBolusDialog : DialogFragmentWithDate() { + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("actions_extendedbolus_insulin", actions_extendedbolus_insulin.value) + savedInstanceState.putDouble("actions_extendedbolus_duration", actions_extendedbolus_duration.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_extendedbolus, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val pumpDescription = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription ?: return + + val maxInsulin = MainApp.getConstraintChecker().maxExtendedBolusAllowed.value() + val extendedStep = pumpDescription.extendedBolusStep + actions_extendedbolus_insulin.setParams(savedInstanceState?.getDouble("actions_extendedbolus_insulin") + ?: extendedStep, extendedStep, maxInsulin, extendedStep, DecimalFormat("0.00"), false, ok) + + val extendedDurationStep = pumpDescription.extendedBolusDurationStep + val extendedMaxDuration = pumpDescription.extendedBolusMaxDuration + actions_extendedbolus_duration.setParams(savedInstanceState?.getDouble("actions_extendedbolus_duration") + ?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, ok) + } + + override fun submit(): Boolean { + val insulin = SafeParse.stringToDouble(actions_extendedbolus_insulin.text) + val durationInMinutes = SafeParse.stringToInt(actions_extendedbolus_duration.text) + val actions: LinkedList = LinkedList() + val insulinAfterConstraint = MainApp.getConstraintChecker().applyExtendedBolusConstraints(Constraint(insulin)).value() + actions.add(MainApp.gs(R.string.formatinsulinunits, insulinAfterConstraint)) + actions.add(MainApp.gs(R.string.duration) + ": " + MainApp.gs(R.string.format_mins, durationInMinutes)) + if (abs(insulinAfterConstraint - insulin) > 0.01) + actions.add("" + MainApp.gs(R.string.constraintapllied) + "") + + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + log.debug("USER ENTRY: EXTENDED BOLUS $insulinAfterConstraint duration: $durationInMinutes") + ConfigBuilderPlugin.getPlugin().commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + }, null) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt new file mode 100644 index 0000000000..a840384de0 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt @@ -0,0 +1,172 @@ +package info.nightscout.androidaps.dialogs + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.common.base.Joiner +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.* +import kotlinx.android.synthetic.main.dialog_fill.* +import kotlinx.android.synthetic.main.notes.* +import kotlinx.android.synthetic.main.okcancel.* +import org.json.JSONException +import org.json.JSONObject +import java.util.* +import kotlin.math.abs + +class FillDialog : DialogFragmentWithDate() { + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("fill_insulinamount", fill_insulinamount.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_fill, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val maxInsulin = MainApp.getConstraintChecker().maxBolusAllowed.value() + val bolusStep = ConfigBuilderPlugin.getPlugin().activePump!!.pumpDescription.bolusStep + fill_insulinamount.setParams(savedInstanceState?.getDouble("fill_insulinamount") + ?: 0.0, 0.0, maxInsulin, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), true, ok) + val amount1 = SP.getDouble("fill_button1", 0.3) + if (amount1 > 0) { + fill_preset_button1.visibility = View.VISIBLE + fill_preset_button1.text = DecimalFormatter.toPumpSupportedBolus(amount1) // + "U"); + fill_preset_button1.setOnClickListener { fill_insulinamount.value = amount1 } + } else { + fill_preset_button1.visibility = View.GONE + } + val amount2 = SP.getDouble("fill_button2", 0.0) + if (amount2 > 0) { + fill_preset_button2.visibility = View.VISIBLE + fill_preset_button2.text = DecimalFormatter.toPumpSupportedBolus(amount2) // + "U"); + fill_preset_button2.setOnClickListener { fill_insulinamount.value = amount2 } + } else { + fill_preset_button2.visibility = View.GONE + } + val amount3 = SP.getDouble("fill_button3", 0.0) + if (amount3 > 0) { + fill_preset_button3.visibility = View.VISIBLE + fill_preset_button3.text = DecimalFormatter.toPumpSupportedBolus(amount3) // + "U"); + fill_preset_button3.setOnClickListener { fill_insulinamount.value = amount3 } + } else { + fill_preset_button3.visibility = View.GONE + } + + } + + override fun submit(): Boolean { + val insulin = SafeParse.stringToDouble(fill_insulinamount.text) + val actions: LinkedList = LinkedList() + + val insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(insulin)).value() + if (insulinAfterConstraints > 0) { + actions.add(MainApp.gs(R.string.fillwarning)) + actions.add("") + actions.add(MainApp.gs(R.string.bolus) + ": " + "" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + MainApp.gs(R.string.insulin_unit_shortname) + "") + if (abs(insulinAfterConstraints - insulin) > 0.01) + actions.add(MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints)) + } + val siteChange = fill_catheter_change.isChecked + if (siteChange) + actions.add("" + "" + MainApp.gs(R.string.record_pump_site_change) + "") + val insulinChange = fill_cartridge_change.isChecked + if (insulinChange) + actions.add("" + "" + MainApp.gs(R.string.record_insulin_cartridge_change) + "") + val notes = notes.text.toString() + if (notes.isNotEmpty()) + actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) + eventTime -= eventTime % 1000 + + if (eventTimeChanged) + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(eventTime)) + + if (insulinAfterConstraints > 0 || fill_catheter_change.isChecked || fill_cartridge_change.isChecked) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + if (insulinAfterConstraints > 0) { + log.debug("USER ENTRY: PRIME BOLUS $insulinAfterConstraints") + requestPrimeBolus(insulinAfterConstraints, notes) + } + if (siteChange) { + log.debug("USER ENTRY: SITE CHANGE") + generateCareportalEvent(CareportalEvent.SITECHANGE, eventTime, notes) + } + if (insulinChange) { + // add a second for case of both checked + log.debug("USER ENTRY: INSULIN CHANGE") + generateCareportalEvent(CareportalEvent.INSULINCHANGE, eventTime + 1000, notes) + } + }, null) + } + } else { + activity?.let { activity -> + OKDialog.show(activity, MainApp.gs(R.string.primefill), MainApp.gs(R.string.no_action_selected)) + } + } + dismiss() + return true + } + + private fun requestPrimeBolus(insulin: Double, notes: String) { + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.insulin = insulin + detailedBolusInfo.context = context + detailedBolusInfo.source = Source.USER + detailedBolusInfo.isValid = false // do not count it in IOB (for pump history) + detailedBolusInfo.notes = notes + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + } + + private fun generateCareportalEvent(eventType: String, time: Long, notes: String) { + val careportalEvent = CareportalEvent() + careportalEvent.source = Source.USER + careportalEvent.date = time + careportalEvent.json = generateJson(eventType, time, notes).toString() + careportalEvent.eventType = eventType + MainApp.getDbHelper().createOrUpdate(careportalEvent) + NSUpload.uploadEvent(eventType, time, notes) + } + + private fun generateJson(careportalEvent: String, time: Long, notes: String): JSONObject { + val data = JSONObject() + try { + data.put("eventType", careportalEvent) + data.put("created_at", DateUtil.toISOString(time)) + data.put("mills", time) + data.put("enteredBy", SP.getString("careportal_enteredby", MainApp.gs(R.string.app_name))) + if (notes.isNotEmpty()) data.put("notes", notes) + } catch (ignored: JSONException) { + } + return data + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt new file mode 100644 index 0000000000..42bc395b8b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -0,0 +1,196 @@ +package info.nightscout.androidaps.dialogs + +import android.content.Intent +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.common.base.Joiner +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.db.TempTarget +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.* +import kotlinx.android.synthetic.main.dialog_insulin.* +import kotlinx.android.synthetic.main.notes.* +import kotlinx.android.synthetic.main.okcancel.* +import java.text.DecimalFormat +import java.util.* +import kotlin.math.abs +import kotlin.math.max + +class InsulinDialog : DialogFragmentWithDate() { + + companion object { + private const val PLUS1_DEFAULT = 0.5 + private const val PLUS2_DEFAULT = 1.0 + private const val PLUS3_DEFAULT = 2.0 + } + + private val maxInsulin = MainApp.getConstraintChecker().maxBolusAllowed.value() + + private val textWatcher: TextWatcher = object : TextWatcher { + override fun afterTextChanged(s: Editable) { + validateInputs() + } + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + } + + private fun validateInputs() { + if (abs(overview_insulin_time.value.toInt()) > 12 * 60) { + overview_insulin_time.value = 0.0 + ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.constraintapllied)) + } + if (overview_insulin_amount.value > maxInsulin) { + overview_insulin_amount.value = 0.0 + ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.bolusconstraintapplied)) + } + } + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("overview_insulin_time", overview_insulin_time.value) + savedInstanceState.putDouble("overview_insulin_amount", overview_insulin_amount.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_insulin, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + overview_insulin_time.setParams(savedInstanceState?.getDouble("overview_insulin_time") + ?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) + overview_insulin_amount.setParams(savedInstanceState?.getDouble("overview_insulin_amount") + ?: 0.0, 0.0, maxInsulin, ConfigBuilderPlugin.getPlugin().activePump!!.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, ok, textWatcher) + + overview_insulin_plus05.text = toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)) + overview_insulin_plus05.setOnClickListener { + overview_insulin_amount.value = max(0.0, overview_insulin_amount.value + + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)) + validateInputs() + } + overview_insulin_plus10.text = toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)) + overview_insulin_plus10.setOnClickListener { + overview_insulin_amount.value = max(0.0, overview_insulin_amount.value + + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)) + validateInputs() + } + overview_insulin_plus20.text = toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)) + overview_insulin_plus20.setOnClickListener { + overview_insulin_amount.value = Math.max(0.0, overview_insulin_amount.value + + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)) + validateInputs() + } + + overview_insulin_time_layout.visibility = View.GONE + overview_insulin_record_only.setOnCheckedChangeListener { _, isChecked: Boolean -> + overview_insulin_time_layout.visibility = isChecked.toVisibility() + } + } + + private fun toSignedString(value: Double): String { + val formatted = DecimalFormatter.toPumpSupportedBolus(value) + return if (value > 0) "+$formatted" else formatted + } + + override fun submit(): Boolean { + val pumpDescription = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription + ?: return false + val insulin = SafeParse.stringToDouble(overview_insulin_amount.text) + val insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(insulin)).value() + val actions: LinkedList = LinkedList() + val units = ProfileFunctions.getSystemUnits() + val unitLabel = if (units == Constants.MMOL) MainApp.gs(R.string.mmol) else MainApp.gs(R.string.mgdl) + val recordOnlyChecked = overview_insulin_record_only.isChecked + val eatingSoonChecked = overview_insulin_start_eating_soon_tt.isChecked + + if (insulinAfterConstraints > 0) { + actions.add(MainApp.gs(R.string.bolus) + ": " + "" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + MainApp.gs(R.string.insulin_unit_shortname) + "") + if (recordOnlyChecked) + actions.add("" + MainApp.gs(R.string.bolusrecordedonly) + "") + if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) + actions.add(MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints)) + } + val eatingSoonTTDuration = DefaultValueHelper.determineEatingSoonTTDuration() + val eatingSoonTT = DefaultValueHelper.determineEatingSoonTT() + if (eatingSoonChecked) + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + eatingSoonTTDuration + " " + MainApp.gs(R.string.unit_minute_short) + ")") + + val timeOffset = overview_insulin_time.value.toInt() + val time = DateUtil.now() + T.mins(timeOffset.toLong()).msecs() + if (timeOffset != 0) + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(time)) + + val notes = notes.text.toString() + if (notes.isNotEmpty()) + actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) + + if (insulinAfterConstraints > 0 || eatingSoonChecked) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + if (eatingSoonChecked) { + log.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration") + val tempTarget = TempTarget() + .date(System.currentTimeMillis()) + .duration(eatingSoonTTDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(eatingSoonTT, ProfileFunctions.getSystemUnits())) + .high(Profile.toMgdl(eatingSoonTT, ProfileFunctions.getSystemUnits())) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + } + if (insulinAfterConstraints > 0) { + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS + detailedBolusInfo.insulin = insulinAfterConstraints + detailedBolusInfo.context = context + detailedBolusInfo.source = Source.USER + detailedBolusInfo.notes = notes + if (recordOnlyChecked) { + log.debug("USER ENTRY: BOLUS RECORD ONLY $insulinAfterConstraints") + detailedBolusInfo.date = time + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false) + } else { + log.debug("USER ENTRY: BOLUS $insulinAfterConstraints") + detailedBolusInfo.date = DateUtil.now() + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + } + } + }) + } + } else + activity?.let { activity -> + OKDialog.show(activity, MainApp.gs(R.string.bolus), MainApp.gs(R.string.no_action_selected)) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt new file mode 100644 index 0000000000..f594564a78 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt @@ -0,0 +1,106 @@ +package info.nightscout.androidaps.dialogs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import com.google.common.base.Joiner +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.OKDialog +import kotlinx.android.synthetic.main.dialog_profileswitch.* +import kotlinx.android.synthetic.main.notes.* +import kotlinx.android.synthetic.main.okcancel.* +import java.text.DecimalFormat +import java.util.* + +class ProfileSwitchDialog : DialogFragmentWithDate() { + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("overview_profileswitch_duration", overview_profileswitch_duration.value) + savedInstanceState.putDouble("overview_profileswitch_percentage", overview_profileswitch_percentage.value) + savedInstanceState.putDouble("overview_profileswitch_timeshift", overview_profileswitch_timeshift.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_profileswitch, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + overview_profileswitch_duration.setParams(savedInstanceState?.getDouble("overview_profileswitch_duration") + ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) + overview_profileswitch_percentage.setParams(savedInstanceState?.getDouble("overview_profileswitch_percentage") + ?: 100.0, Constants.CPP_MIN_PERCENTAGE.toDouble(), Constants.CPP_MAX_PERCENTAGE.toDouble(), 1.0, DecimalFormat("0"), false, ok) + overview_profileswitch_timeshift.setParams(savedInstanceState?.getDouble("overview_profileswitch_timeshift") + ?: 0.0, Constants.CPP_MIN_TIMESHIFT.toDouble(), Constants.CPP_MAX_TIMESHIFT.toDouble(), 1.0, DecimalFormat("0"), false, ok) + + // profile + context?.let { context -> + val profileStore = ConfigBuilderPlugin.getPlugin().activeProfileInterface?.profile + ?: return + val profileList = profileStore.getProfileList() + val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) + overview_profileswitch_profile.adapter = adapter + // set selected to actual profile + for (p in profileList.indices) + if (profileList[p] == ProfileFunctions.getInstance().getProfileName(false)) + overview_profileswitch_profile.setSelection(p) + } ?: return + + TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now())?.let { ps -> + if (ps.isCPP) { + overview_profileswitch_reuselayout.visibility = View.VISIBLE + overview_profileswitch_reusebutton.text = MainApp.gs(R.string.reuse) + " " + ps.percentage + "% " + ps.timeshift + "h" + overview_profileswitch_reusebutton.setOnClickListener { + overview_profileswitch_percentage.value = ps.percentage.toDouble() + overview_profileswitch_timeshift.value = ps.timeshift.toDouble() + } + } else { + overview_profileswitch_reuselayout.visibility = View.GONE + } + } + } + + override fun submit(): Boolean { + val profileStore = ConfigBuilderPlugin.getPlugin().activeProfileInterface?.profile + ?: return false + + val actions: LinkedList = LinkedList() + val duration = overview_profileswitch_duration.value + if (duration > 0) + actions.add(MainApp.gs(R.string.duration) + ": " + MainApp.gs(R.string.format_hours, duration)) + val profile = overview_profileswitch_profile.selectedItem.toString() + actions.add(MainApp.gs(R.string.profile) + ": " + profile) + val percent = overview_profileswitch_percentage.value.toInt() + if (percent != 100) + actions.add(MainApp.gs(R.string.percent) + ": " + percent + "%") + val timeShift = overview_profileswitch_timeshift.value.toInt() + if (timeShift != 0) + actions.add(MainApp.gs(R.string.careportal_newnstreatment_timeshift_label) + ": " + MainApp.gs(R.string.format_hours, timeShift.toDouble())) + val notes = notes.text.toString() + if (notes.isNotEmpty()) + actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) + if (eventTimeChanged) + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(eventTime)) + + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + log.debug("USER ENTRY: PROFILE SWITCH $profile percent: $percent timeshift: $timeShift duration: $duration") + ProfileFunctions.doProfileSwitch(profileStore, profile, duration.toInt(), percent, timeShift, eventTime) + }) + } + return true + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt similarity index 66% rename from app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt rename to app/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt index a27ebfb656..ef3b19d4d8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt @@ -1,29 +1,34 @@ -package info.nightscout.androidaps.plugins.treatments.fragments +package info.nightscout.androidaps.dialogs import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.Window +import android.view.WindowManager import androidx.fragment.app.DialogFragment +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.ProfileInterface -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil import kotlinx.android.synthetic.main.close.* -import kotlinx.android.synthetic.main.profileviewer_fragment.* +import kotlinx.android.synthetic.main.dialog_profileviewer.* +import org.json.JSONObject class ProfileViewerDialog : DialogFragment() { private var time: Long = 0 enum class Mode(val i: Int) { RUNNING_PROFILE(1), - PUMP_PROFILE(2) + CUSTOM_PROFILE(2) } - private var mode: Mode = Mode.RUNNING_PROFILE; + private var mode: Mode = Mode.RUNNING_PROFILE + private var customProfileJson: String = "" + private var customProfileName: String = "" + private var customProfileUnits: String = Constants.MGDL override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -31,19 +36,23 @@ class ProfileViewerDialog : DialogFragment() { (savedInstanceState ?: arguments)?.let { bundle -> time = bundle.getLong("time", 0) mode = Mode.values()[bundle.getInt("mode", Mode.RUNNING_PROFILE.ordinal)] + customProfileJson = bundle.getString("customProfile", "") + customProfileUnits = bundle.getString("customProfileUnits", Constants.MGDL) + customProfileName = bundle.getString("customProfileName", "") } - return inflater.inflate(R.layout.profileviewer_fragment, container, false) + dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) + dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) + isCancelable = true + dialog?.setCanceledOnTouchOutside(false) + + return inflater.inflate(R.layout.dialog_profileviewer, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) close.setOnClickListener { dismiss() } - profileview_reload.setOnClickListener { - ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("ProfileViewDialog", null) - dismiss() - } val profile: Profile? val profileName: String? @@ -53,15 +62,14 @@ class ProfileViewerDialog : DialogFragment() { profile = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(time)?.profileObject profileName = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(time)?.customizedName date = DateUtil.dateAndTimeString(TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(time)?.date - ?: 0) - profileview_reload.visibility = View.GONE + ?: 0) profileview_datelayout.visibility = View.VISIBLE } - Mode.PUMP_PROFILE -> { - profile = (ConfigBuilderPlugin.getPlugin().activePump as ProfileInterface?)?.profile?.defaultProfile - profileName = (ConfigBuilderPlugin.getPlugin().activePump as ProfileInterface?)?.profileName + + Mode.CUSTOM_PROFILE -> { + profile = Profile(JSONObject(customProfileJson), customProfileUnits) + profileName = customProfileName date = "" - profileview_reload.visibility = View.VISIBLE profileview_datelayout.visibility = View.GONE } } @@ -83,15 +91,18 @@ class ProfileViewerDialog : DialogFragment() { } } - override fun onResume() { + override fun onStart() { + super.onStart() dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) - super.onResume() } override fun onSaveInstanceState(bundle: Bundle) { super.onSaveInstanceState(bundle) bundle.putLong("time", time) bundle.putInt("mode", mode.ordinal) + bundle.putString("customProfile", customProfileJson) + bundle.putString("customProfileName", customProfileName) + bundle.putString("customProfileUnits", customProfileUnits) } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt new file mode 100644 index 0000000000..d821aec56b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt @@ -0,0 +1,117 @@ +package info.nightscout.androidaps.dialogs + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.common.base.Joiner +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.PumpDescription +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.SafeParse +import kotlinx.android.synthetic.main.dialog_tempbasal.* +import kotlinx.android.synthetic.main.okcancel.* +import java.text.DecimalFormat +import java.util.* +import kotlin.math.abs + +class TempBasalDialog : DialogFragmentWithDate() { + private var isPercentPump = true + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("actions_tempbasal_duration", actions_tempbasal_duration.value) + savedInstanceState.putDouble("actions_tempbasal_basalpercentinput", actions_tempbasal_basalpercentinput.value) + savedInstanceState.putDouble("actions_tempbasal_basalabsoluteinput", actions_tempbasal_basalabsoluteinput.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_tempbasal, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val pumpDescription = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription ?: return + val profile = ProfileFunctions.getInstance().profile ?: return + + val maxTempPercent = pumpDescription.maxTempPercent.toDouble() + val tempPercentStep = pumpDescription.tempPercentStep.toDouble() + + actions_tempbasal_basalpercentinput.setParams(savedInstanceState?.getDouble("actions_tempbasal_basalpercentinput") + ?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, ok) + + actions_tempbasal_basalabsoluteinput.setParams(savedInstanceState?.getDouble("actions_tempbasal_basalabsoluteinput") + ?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, ok) + + val tempDurationStep = pumpDescription.tempDurationStep.toDouble() + val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble() + actions_tempbasal_duration.setParams(savedInstanceState?.getDouble("actions_tempbasal_duration") + ?: tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, DecimalFormat("0"), false, ok) + + isPercentPump = pumpDescription.tempBasalStyle and PumpDescription.PERCENT == PumpDescription.PERCENT + if (isPercentPump) { + actions_tempbasal_percent_layout.visibility = View.VISIBLE + actions_tempbasal_absolute_layout.visibility = View.GONE + } else { + actions_tempbasal_percent_layout.visibility = View.GONE + actions_tempbasal_absolute_layout.visibility = View.VISIBLE + } + } + + override fun submit(): Boolean { + var percent = 0 + var absolute = 0.0 + val durationInMinutes = SafeParse.stringToInt(actions_tempbasal_duration.text) + val profile = ProfileFunctions.getInstance().profile ?: return false + val actions: LinkedList = LinkedList() + if (isPercentPump) { + val basalPercentInput = SafeParse.stringToInt(actions_tempbasal_basalpercentinput.text) + percent = MainApp.getConstraintChecker().applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value() + actions.add(MainApp.gs(R.string.pump_tempbasal_label)+ ": $percent%") + actions.add(MainApp.gs(R.string.duration) + ": " + MainApp.gs(R.string.format_mins, durationInMinutes)) + if (percent != basalPercentInput) actions.add(MainApp.gs(R.string.constraintapllied)) + } else { + val basalAbsoluteInput = SafeParse.stringToDouble(actions_tempbasal_basalabsoluteinput.text) + absolute = MainApp.getConstraintChecker().applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value() + actions.add(MainApp.gs(R.string.pump_tempbasal_label)+ ": " + MainApp.gs(R.string.pump_basebasalrate, absolute)) + actions.add(MainApp.gs(R.string.duration) + ": " + MainApp.gs(R.string.format_mins, durationInMinutes)) + if (abs(absolute - basalAbsoluteInput) > 0.01) + actions.add("" + MainApp.gs(R.string.constraintapllied) + "") + } + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.pump_tempbasal_label), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + val callback: Callback = object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + } + if (isPercentPump) { + log.debug("USER ENTRY: TEMP BASAL $percent% duration: $durationInMinutes") + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback) + } else { + log.debug("USER ENTRY: TEMP BASAL $absolute duration: $durationInMinutes") + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback) + } + }) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt new file mode 100644 index 0000000000..5b1dd48692 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt @@ -0,0 +1,153 @@ +package info.nightscout.androidaps.dialogs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import com.google.common.base.Joiner +import com.google.common.collect.Lists +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.db.Source +import info.nightscout.androidaps.db.TempTarget +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DefaultValueHelper +import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.SP +import kotlinx.android.synthetic.main.dialog_temptarget.* +import kotlinx.android.synthetic.main.okcancel.* +import java.text.DecimalFormat +import java.util.* + +class TempTargetDialog : DialogFragmentWithDate() { + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("overview_temptarget_duration", overview_temptarget_duration.value) + savedInstanceState.putDouble("overview_temptarget_temptarget", overview_temptarget_temptarget.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_temptarget, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + overview_temptarget_duration.setParams(savedInstanceState?.getDouble("overview_temptarget_duration") + ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) + + if (ProfileFunctions.getSystemUnits() == Constants.MMOL) + overview_temptarget_temptarget.setParams( + savedInstanceState?.getDouble("overview_temptarget_temptarget") + ?: Constants.MIN_TT_MMOL, + Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, ok) + else + overview_temptarget_temptarget.setParams( + savedInstanceState?.getDouble("overview_temptarget_temptarget") + ?: Constants.MIN_TT_MGDL, + Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, ok) + + val units = ProfileFunctions.getSystemUnits() + overview_temptarget_units.text = if (units == Constants.MMOL) MainApp.gs(R.string.mmol) else MainApp.gs(R.string.mgdl) + // temp target + context?.let { context -> + val reasonList: List = Lists.newArrayList( + MainApp.gs(R.string.manual), + MainApp.gs(R.string.cancel), + MainApp.gs(R.string.eatingsoon), + MainApp.gs(R.string.activity), + MainApp.gs(R.string.hypo) + ) + val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList) + overview_temptarget_reason.adapter = adapterReason + overview_temptarget_reason.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { + val defaultDuration: Double + val defaultTarget: Double + when (reasonList[position]) { + MainApp.gs(R.string.eatingsoon) -> { + defaultDuration = DefaultValueHelper.determineEatingSoonTTDuration().toDouble() + defaultTarget = DefaultValueHelper.determineEatingSoonTT() + } + + MainApp.gs(R.string.activity) -> { + defaultDuration = DefaultValueHelper.determineActivityTTDuration().toDouble() + defaultTarget = DefaultValueHelper.determineActivityTT() + } + + MainApp.gs(R.string.hypo) -> { + defaultDuration = DefaultValueHelper.determineHypoTTDuration().toDouble() + defaultTarget = DefaultValueHelper.determineHypoTT() + } + + MainApp.gs(R.string.cancel) -> { + defaultDuration = 0.0 + defaultTarget = 0.0 + } + + else -> { + defaultDuration = overview_temptarget_duration.value + defaultTarget = overview_temptarget_temptarget.value + } + } + overview_temptarget_temptarget.value = defaultTarget + overview_temptarget_duration.value = defaultDuration + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } + } + + override fun submit(): Boolean { + val actions: LinkedList = LinkedList() + val reason = overview_temptarget_reason.selectedItem.toString() + val unitResId = if (ProfileFunctions.getSystemUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol + val target = overview_temptarget_temptarget.value + val duration = overview_temptarget_duration.value.toInt() + if (target != 0.0 && duration != 0) { + actions.add(MainApp.gs(R.string.reason) + ": " + reason) + actions.add(MainApp.gs(R.string.nsprofileview_target_label) + ": " + Profile.toCurrentUnitsString(target) + " " + MainApp.gs(unitResId)) + actions.add(MainApp.gs(R.string.duration) + ": " + MainApp.gs(R.string.format_mins, duration)) + } else { + actions.add(MainApp.gs(R.string.stoptemptarget)) + } + if (eventTimeChanged) + actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(eventTime)) + + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + log.debug("USER ENTRY: TEMP TARGET $target duration: $duration") + if (target == 0.0 || duration == 0) { + val tempTarget = TempTarget() + .date(eventTime) + .duration(0) + .low(0.0).high(0.0) + .source(Source.USER) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + } else { + val tempTarget = TempTarget() + .date(eventTime) + .duration(duration.toInt()) + .reason(reason) + .source(Source.USER) + .low(Profile.toMgdl(target, ProfileFunctions.getSystemUnits())) + .high(Profile.toMgdl(target, ProfileFunctions.getSystemUnits())) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + } + if (duration == 10) SP.putBoolean(R.string.key_objectiveusetemptarget, true) + }) + } + return true + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt new file mode 100644 index 0000000000..565a65f54f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt @@ -0,0 +1,133 @@ +package info.nightscout.androidaps.dialogs + +import android.content.Intent +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.common.base.Joiner +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.SafeParse +import info.nightscout.androidaps.utils.ToastUtils +import kotlinx.android.synthetic.main.dialog_treatment.* +import kotlinx.android.synthetic.main.okcancel.* +import java.text.DecimalFormat +import java.util.* +import kotlin.math.abs + +class TreatmentDialog : DialogFragmentWithDate() { + private var maxCarbs = MainApp.getConstraintChecker().maxCarbsAllowed.value().toDouble() + private var maxInsulin = MainApp.getConstraintChecker().maxBolusAllowed.value() + + private val textWatcher: TextWatcher = object : TextWatcher { + override fun afterTextChanged(s: Editable) {} + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + validateInputs() + } + } + + private fun validateInputs() { + if (SafeParse.stringToInt(overview_treatment_carbs.text) > maxCarbs) { + overview_treatment_carbs.value = 0.0 + ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.carbsconstraintapplied)) + } + if (SafeParse.stringToDouble(overview_treatment_insulin.text) > maxInsulin) { + overview_treatment_insulin.value = 0.0 + ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.bolusconstraintapplied)) + } + } + + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putDouble("overview_treatment_carbs", overview_treatment_carbs.value) + savedInstanceState.putDouble("overview_treatment_insulin", overview_treatment_insulin.value) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + onCreateViewGeneral() + return inflater.inflate(R.layout.dialog_treatment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val pumpDescription = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription ?: return + overview_treatment_carbs.setParams(savedInstanceState?.getDouble("overview_treatment_carbs") + ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, ok, textWatcher) + overview_treatment_insulin.setParams(savedInstanceState?.getDouble("overview_treatment_insulin") + ?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, ok, textWatcher) + } + + override fun submit(): Boolean { + val pumpDescription = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription + ?: return false + val insulin = SafeParse.stringToDouble(overview_treatment_insulin.text) + val carbs = SafeParse.stringToInt(overview_treatment_carbs.text) + val recordOnlyChecked = overview_treatment_record_only.isChecked + val actions: LinkedList = LinkedList() + val insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(insulin)).value() + val carbsAfterConstraints = MainApp.getConstraintChecker().applyCarbsConstraints(Constraint(carbs)).value() + + if (insulinAfterConstraints > 0) { + actions.add(MainApp.gs(R.string.bolus) + ": " + "" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + MainApp.gs(R.string.insulin_unit_shortname) + "") + if (recordOnlyChecked) + actions.add("" + MainApp.gs(R.string.bolusrecordedonly) + "") + if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) + actions.add(MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints)) + } + if (carbsAfterConstraints > 0) { + actions.add(MainApp.gs(R.string.carbs) + ": " + "" + MainApp.gs(R.string.format_carbs, carbsAfterConstraints) + "") + if (carbsAfterConstraints != carbs) + actions.add("" + MainApp.gs(R.string.carbsconstraintapplied) + "") + } + if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + log.debug("USER ENTRY: BOLUS insulin $insulin carbs: $carbs") + val detailedBolusInfo = DetailedBolusInfo() + if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION + if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS + detailedBolusInfo.insulin = insulinAfterConstraints + detailedBolusInfo.carbs = carbsAfterConstraints.toDouble() + detailedBolusInfo.context = context + detailedBolusInfo.source = Source.USER + if (!(recordOnlyChecked && (detailedBolusInfo.insulin > 0 || pumpDescription.storesCarbInfo))) { + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + } else + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false) + }) + } + } else + activity?.let { activity -> + OKDialog.show(activity, MainApp.gs(R.string.overview_treatment_label), MainApp.gs(R.string.no_action_selected)) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt similarity index 78% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/WizardDialog.kt rename to app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt index 552ab502e7..c84bd4a0b6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt @@ -1,10 +1,13 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs +package info.nightscout.androidaps.dialogs -import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher -import android.view.* +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.Window +import android.view.WindowManager import android.widget.AdapterView import android.widget.AdapterView.OnItemSelectedListener import android.widget.ArrayAdapter @@ -26,7 +29,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.* import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.overview_wizard_dialog.* +import kotlinx.android.synthetic.main.dialog_wizard.* import org.slf4j.LoggerFactory import java.text.DecimalFormat import java.util.* @@ -36,16 +39,13 @@ class WizardDialog : DialogFragment() { private val log = LoggerFactory.getLogger(WizardDialog::class.java) private var wizard: BolusWizard? = null - private var parentContext: Context? = null //one shot guards private var okClicked: Boolean = false private val textWatcher = object : TextWatcher { override fun afterTextChanged(s: Editable) {} - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { calculateInsulin() } @@ -53,21 +53,11 @@ class WizardDialog : DialogFragment() { private var disposable: CompositeDisposable = CompositeDisposable() - override fun onAttach(context: Context) { - super.onAttach(context) - this.parentContext = context - } - override fun onStart() { super.onStart() dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) } - override fun onDetach() { - super.onDetach() - this.parentContext = null - } - override fun onSaveInstanceState(savedInstanceState: Bundle) { super.onSaveInstanceState(savedInstanceState) savedInstanceState.putDouble("treatments_wizard_bg_input", treatments_wizard_bg_input.value) @@ -83,28 +73,28 @@ class WizardDialog : DialogFragment() { isCancelable = true dialog?.setCanceledOnTouchOutside(false) - return inflater.inflate(R.layout.overview_wizard_dialog, container, false) + return inflater.inflate(R.layout.dialog_wizard, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { loadCheckedStates() processCobCheckBox() - treatments_wizard_sbcheckbox.visibility = if (SP.getBoolean(R.string.key_usesuperbolus, false)) View.VISIBLE else View.GONE - treatments_wizard_notes_layout.visibility = if (SP.getBoolean(R.string.key_show_notes_entry_dialogs, false)) View.VISIBLE else View.GONE + treatments_wizard_sbcheckbox.visibility = SP.getBoolean(R.string.key_usesuperbolus, false).toVisibility() + treatments_wizard_notes_layout.visibility = SP.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() val maxCarbs = MainApp.getConstraintChecker().maxCarbsAllowed.value() val maxCorrection = MainApp.getConstraintChecker().maxBolusAllowed.value() treatments_wizard_bg_input.setParams(savedInstanceState?.getDouble("treatments_wizard_bg_input") - ?: 0.0, 0.0, 500.0, 0.1, DecimalFormat("0.0"), false, ok, textWatcher) + ?: 0.0, 0.0, 500.0, 0.1, DecimalFormat("0.0"), false, ok, textWatcher) treatments_wizard_carbs_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carbs_input") - ?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, ok, textWatcher) + ?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, ok, textWatcher) val bolusStep = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.bolusStep - ?: 0.1 + ?: 0.1 treatments_wizard_correction_input.setParams(savedInstanceState?.getDouble("treatments_wizard_correction_input") - ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, ok, textWatcher) + ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, ok, textWatcher) treatments_wizard_carb_time_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carb_time_input") - ?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) + ?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) initDialog() treatments_wizard_percent_used.text = MainApp.gs(R.string.format_percent, SP.getInt(R.string.key_boluswizard_percentage, 100)) @@ -114,7 +104,8 @@ class WizardDialog : DialogFragment() { log.debug("guarding: ok already clicked") } else { okClicked = true - parentContext?.let { context -> + calculateInsulin() + context?.let { context -> wizard?.confirmAndExecute(context) } } @@ -123,23 +114,23 @@ class WizardDialog : DialogFragment() { // cancel button cancel.setOnClickListener { dismiss() } // checkboxes - treatments_wizard_bgcheckbox.setOnCheckedChangeListener { buttonView, _ -> onCheckedChanged(buttonView) } - treatments_wizard_ttcheckbox.setOnCheckedChangeListener { buttonView, _ -> onCheckedChanged(buttonView) } - treatments_wizard_cobcheckbox.setOnCheckedChangeListener { buttonView, _ -> onCheckedChanged(buttonView) } - treatments_wizard_basaliobcheckbox.setOnCheckedChangeListener { buttonView, _ -> onCheckedChanged(buttonView) } - treatments_wizard_bolusiobcheckbox.setOnCheckedChangeListener { buttonView, _ -> onCheckedChanged(buttonView) } - treatments_wizard_bgtrendcheckbox.setOnCheckedChangeListener { buttonView, _ -> onCheckedChanged(buttonView) } - treatments_wizard_sbcheckbox.setOnCheckedChangeListener { buttonView, _ -> onCheckedChanged(buttonView) } + treatments_wizard_bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + treatments_wizard_ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + treatments_wizard_cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + treatments_wizard_basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + treatments_wizard_bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + treatments_wizard_bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + treatments_wizard_sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged) val showCalc = SP.getBoolean(MainApp.gs(R.string.key_wizard_calculation_visible), false) - treatments_wizard_delimiter.visibility = if (showCalc) View.VISIBLE else View.GONE - treatments_wizard_resulttable.visibility = if (showCalc) View.VISIBLE else View.GONE + treatments_wizard_delimiter.visibility = showCalc.toVisibility() + treatments_wizard_resulttable.visibility = showCalc.toVisibility() treatments_wizard_calculationcheckbox.isChecked = showCalc treatments_wizard_calculationcheckbox.setOnCheckedChangeListener { _, isChecked -> run { SP.putBoolean(MainApp.gs(R.string.key_wizard_calculation_visible), isChecked) - treatments_wizard_delimiter.visibility = if (isChecked) View.VISIBLE else View.GONE - treatments_wizard_resulttable.visibility = if (isChecked) View.VISIBLE else View.GONE + treatments_wizard_delimiter.visibility = isChecked.toVisibility() + treatments_wizard_resulttable.visibility = isChecked.toVisibility() } } // profile spinner @@ -156,13 +147,13 @@ class WizardDialog : DialogFragment() { } // bus disposable.add(RxBus - .toObservable(EventAutosensCalculationFinished::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - activity?.runOnUiThread { calculateInsulin() } - }, { - FabricPrivacy.logException(it) - }) + .toObservable(EventAutosensCalculationFinished::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + activity?.runOnUiThread { calculateInsulin() } + }, { + FabricPrivacy.logException(it) + }) ) } @@ -172,7 +163,7 @@ class WizardDialog : DialogFragment() { disposable.clear() } - private fun onCheckedChanged(buttonView: CompoundButton) { + private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) { saveCheckedStates() treatments_wizard_ttcheckbox.isEnabled = treatments_wizard_bgcheckbox.isChecked && TreatmentsPlugin.getPlugin().tempTargetFromHistory != null if (buttonView.id == treatments_wizard_cobcheckbox.id) @@ -213,15 +204,14 @@ class WizardDialog : DialogFragment() { } val profileList: ArrayList - profileList = profileStore.profileList + profileList = profileStore.getProfileList() profileList.add(0, MainApp.gs(R.string.active)) context?.let { context -> val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) treatments_wizard_profile.adapter = adapter } ?: return - - val units = profile.units + val units = ProfileFunctions.getSystemUnits() treatments_wizard_bgunits.text = units if (units == Constants.MGDL) treatments_wizard_bg_input.setStep(1.0) @@ -249,7 +239,7 @@ class WizardDialog : DialogFragment() { calculateInsulin() - treatments_wizard_percent_used.visibility = if (SP.getInt(R.string.key_boluswizard_percentage, 100) != 100) View.VISIBLE else View.GONE + treatments_wizard_percent_used.visibility = (SP.getInt(R.string.key_boluswizard_percentage, 100) != 100).toVisibility() } private fun calculateInsulin() { @@ -290,18 +280,18 @@ class WizardDialog : DialogFragment() { val carbTime = SafeParse.stringToInt(treatments_wizard_carb_time_input.text) wizard = BolusWizard(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, - SP.getInt(R.string.key_boluswizard_percentage, 100).toDouble(), - treatments_wizard_bgcheckbox.isChecked, - treatments_wizard_cobcheckbox.isChecked, - treatments_wizard_bolusiobcheckbox.isChecked, - treatments_wizard_basaliobcheckbox.isChecked, - treatments_wizard_sbcheckbox.isChecked, - treatments_wizard_ttcheckbox.isChecked, - treatments_wizard_bgtrendcheckbox.isChecked, - treatment_wizard_notes.text.toString(), carbTime) + SP.getInt(R.string.key_boluswizard_percentage, 100).toDouble(), + treatments_wizard_bgcheckbox.isChecked, + treatments_wizard_cobcheckbox.isChecked, + treatments_wizard_bolusiobcheckbox.isChecked, + treatments_wizard_basaliobcheckbox.isChecked, + treatments_wizard_sbcheckbox.isChecked, + treatments_wizard_ttcheckbox.isChecked, + treatments_wizard_bgtrendcheckbox.isChecked, + treatment_wizard_notes.text.toString(), carbTime) wizard?.let { wizard -> - treatments_wizard_bg.text = String.format(MainApp.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, specificProfile.units)).valueToUnitsToString(specificProfile.units), wizard.sens) + treatments_wizard_bg.text = String.format(MainApp.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, ProfileFunctions.getSystemUnits())).valueToUnitsToString(ProfileFunctions.getSystemUnits()), wizard.sens) treatments_wizard_bginsulin.text = StringUtils.formatInsulin(wizard.insulinFromBG) treatments_wizard_carbs.text = String.format(MainApp.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic) @@ -319,8 +309,8 @@ class WizardDialog : DialogFragment() { // Trend if (treatments_wizard_bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) { treatments_wizard_bgtrend.text = ((if (wizard.trend > 0) "+" else "") - + Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, specificProfile.units) - + " " + specificProfile.units) + + Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, ProfileFunctions.getSystemUnits()) + + " " + ProfileFunctions.getSystemUnits()) } else { treatments_wizard_bgtrend.text = "" } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java index f7c8e9ada4..5d4ec01fd9 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java @@ -1,10 +1,12 @@ package info.nightscout.androidaps.interfaces; import android.os.SystemClock; +import android.preference.Preference; +import android.preference.PreferenceFragment; -import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentActivity; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,6 +19,7 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.EventConfigBuilderUpdateGui; import info.nightscout.androidaps.queue.CommandQueue; +import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; /** @@ -55,27 +58,23 @@ public abstract class PluginBase { if (allowHardwarePump || activity == null) { performPluginSwitch(newState, type); } else { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setMessage(R.string.allow_hardware_pump_text) - .setPositiveButton(R.string.yes, (dialog, id) -> { - performPluginSwitch(newState, type); - SP.putBoolean("allow_hardware_pump", true); - if (L.isEnabled(L.PUMP)) - log.debug("First time HW pump allowed!"); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> { - RxBus.INSTANCE.send(new EventConfigBuilderUpdateGui()); - if (L.isEnabled(L.PUMP)) - log.debug("User does not allow switching to HW pump!"); - }); - builder.create().show(); + OKDialog.showConfirmation(activity, MainApp.gs(R.string.allow_hardware_pump_text), () -> { + performPluginSwitch(newState, type); + SP.putBoolean("allow_hardware_pump", true); + if (L.isEnabled(L.PUMP)) + log.debug("First time HW pump allowed!"); + }, () -> { + RxBus.INSTANCE.send(new EventConfigBuilderUpdateGui()); + if (L.isEnabled(L.PUMP)) + log.debug("User does not allow switching to HW pump!"); + }); } } else { performPluginSwitch(newState, type); } } - private void performPluginSwitch(boolean enabled, PluginType type) { + public void performPluginSwitch(boolean enabled, PluginType type) { setPluginEnabled(type, enabled); setFragmentVisible(type, enabled); ConfigBuilderPlugin.getPlugin().processOnEnabledCategoryChanged(this, getType()); @@ -215,4 +214,10 @@ public abstract class PluginBase { protected void onStateChange(PluginType type, State oldState, State newState) { } + + public void preprocessPreferences(@NotNull final PreferenceFragment preferenceFragment) { + } + + public void updatePreferenceSummary(@NotNull final Preference pref) { + } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java index 5882c5870c..2929d3f21b 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java @@ -29,8 +29,8 @@ public class PluginDescription { return this; } - public PluginDescription alwaysVisible(boolean alwayVisible) { - this.alwaysVisible = alwayVisible; + public PluginDescription alwaysVisible(boolean alwaysVisible) { + this.alwaysVisible = alwaysVisible; return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java index 482278e437..5fb3b459b6 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java @@ -10,6 +10,5 @@ import info.nightscout.androidaps.data.ProfileStore; public interface ProfileInterface { @Nullable ProfileStore getProfile(); - String getUnits(); String getProfileName(); } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 43661b117a..cbdd5d696a 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -31,7 +31,7 @@ public interface TreatmentsInterface { MealData getMealData(); List getTreatmentsFromHistory(); - List getTreatments5MinBackFromHistory(long time); + List getCarbTreatments5MinBackFromHistory(long time); List getTreatmentsFromHistoryAfterTimestamp(long timestamp); long getLastBolusTime(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java index b3b0c091d5..7e568af2d9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java @@ -247,7 +247,7 @@ public class APSResult { } } } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return array; } @@ -280,7 +280,7 @@ public class APSResult { } } } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return latest; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/DeviceStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/DeviceStatus.java index 69afe3d8e7..26d7359efe 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/DeviceStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/DeviceStatus.java @@ -381,13 +381,11 @@ public class DeviceStatus { try { if (device != null) record.put("device", device); if (pump != null) record.put("pump", pump); - if (suggested != null) { - JSONObject openaps = new JSONObject(); - if (enacted != null) openaps.put("enacted", enacted); - if (suggested != null) openaps.put("suggested", suggested); - if (iob != null) openaps.put("iob", iob); - record.put("openaps", openaps); - } + JSONObject openaps = new JSONObject(); + if (enacted != null) openaps.put("enacted", enacted); + if (suggested != null) openaps.put("suggested", suggested); + if (iob != null) openaps.put("iob", iob); + record.put("openaps", openaps); if (uploaderBattery != 0) record.put("uploaderBattery", uploaderBattery); if (created_at != null) record.put("created_at", created_at); } catch (JSONException e) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt index b7549fcfed..5625fd978f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.aps.loop - import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -12,7 +11,11 @@ import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui import info.nightscout.androidaps.plugins.bus.RxBus -import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.SP +import info.nightscout.androidaps.utils.plusAssign import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.loop_fragment.* @@ -39,23 +42,21 @@ class LoopFragment : Fragment() { override fun onResume() { super.onResume() disposable += RxBus - .toObservable(EventLoopUpdateGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateGUI() - }, { - FabricPrivacy.logException(it) - }) + .toObservable(EventLoopUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + updateGUI() + }, { + FabricPrivacy.logException(it) + }) disposable += RxBus - .toObservable(EventLoopSetLastRunGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - clearGUI() - loop_lastrun.text = it.text - }, { - FabricPrivacy.logException(it) - }) + .toObservable(EventLoopSetLastRunGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + clearGUI() + loop_lastrun?.text = it.text + }, { FabricPrivacy.logException(it) }) updateGUI() SP.putBoolean(R.string.key_objectiveuseloop, true) @@ -71,39 +72,44 @@ class LoopFragment : Fragment() { fun updateGUI() { if (loop_request == null) return LoopPlugin.lastRun?.let { - loop_request.text = it.request?.toSpanned() ?: "" - loop_constraintsprocessed.text = it.constraintsProcessed?.toSpanned() ?: "" - loop_source.text = it.source ?: "" - loop_lastrun.text = it.lastAPSRun?.let { lastRun -> DateUtil.dateAndTimeString(lastRun.time) } - ?: "" - loop_lastenact.text = it.lastAPSRun?.let { lastEnact -> DateUtil.dateAndTimeString(lastEnact.time) } - ?: "" - loop_tbrsetbypump.text = it.tbrSetByPump?.let { tbrSetByPump -> HtmlHelper.fromHtml(tbrSetByPump.toHtml()) } - ?: "" - loop_smbsetbypump.text = it.smbSetByPump?.let { smbSetByPump -> HtmlHelper.fromHtml(smbSetByPump.toHtml()) } - ?: "" + loop_request?.text = it.request?.toSpanned() ?: "" + loop_constraintsprocessed?.text = it.constraintsProcessed?.toSpanned() ?: "" + loop_source?.text = it.source ?: "" + loop_lastrun?.text = it.lastAPSRun?.let { lastRun -> DateUtil.dateAndTimeString(lastRun.time) } + ?: "" + loop_smbrequest_time?.text = DateUtil.dateAndTimeAndSecondsString(it.lastSMBRequest) + loop_smbexecution_time?.text = DateUtil.dateAndTimeAndSecondsString(it.lastSMBEnact) + loop_tbrrequest_time?.text = DateUtil.dateAndTimeAndSecondsString(it.lastTBRRequest) + loop_tbrexecution_time?.text = DateUtil.dateAndTimeAndSecondsString(it.lastTBREnact) + + loop_tbrsetbypump?.text = it.tbrSetByPump?.let { tbrSetByPump -> HtmlHelper.fromHtml(tbrSetByPump.toHtml()) } + ?: "" + loop_smbsetbypump?.text = it.smbSetByPump?.let { smbSetByPump -> HtmlHelper.fromHtml(smbSetByPump.toHtml()) } + ?: "" val constraints = - it.constraintsProcessed?.let { constraintsProcessed -> - val allConstraints = Constraint(0.0) - constraintsProcessed.rateConstraint?.let { rateConstraint -> allConstraints.copyReasons(rateConstraint) } - constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) } - allConstraints.mostLimitedReasons - } ?: "" - loop_constraints.text = constraints + it.constraintsProcessed?.let { constraintsProcessed -> + val allConstraints = Constraint(0.0) + constraintsProcessed.rateConstraint?.let { rateConstraint -> allConstraints.copyReasons(rateConstraint) } + constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) } + allConstraints.mostLimitedReasons + } ?: "" + loop_constraints?.text = constraints } } @Synchronized private fun clearGUI() { - if (loop_request == null) return - loop_request.text = "" - loop_constraints.text = "" - loop_constraintsprocessed.text = "" - loop_source.text = "" - loop_lastrun.text = "" - loop_lastenact.text = "" - loop_tbrsetbypump.text = "" - loop_smbsetbypump.text = "" + loop_request?.text = "" + loop_constraints?.text = "" + loop_constraintsprocessed?.text = "" + loop_source?.text = "" + loop_lastrun?.text = "" + loop_smbrequest_time?.text = "" + loop_smbexecution_time?.text = "" + loop_tbrrequest_time?.text = "" + loop_tbrexecution_time?.text = "" + loop_tbrsetbypump?.text = "" + loop_smbsetbypump?.text = "" } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java index f339bd3771..4710cf9b64 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java @@ -14,6 +14,8 @@ import android.os.SystemClock; import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; +import org.json.JSONException; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,17 +52,17 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.commands.Command; +import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; -import info.nightscout.androidaps.utils.ToastUtils; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; @@ -85,9 +87,9 @@ public class LoopPlugin extends PluginBase { return loopPlugin; } - private long loopSuspendedTill = 0L; // end of manual loop suspend - private boolean isSuperBolus = false; - private boolean isDisconnected = false; + private long loopSuspendedTill; // end of manual loop suspend + private boolean isSuperBolus; + private boolean isDisconnected; public class LastRun { public APSResult request = null; @@ -96,8 +98,11 @@ public class LoopPlugin extends PluginBase { public PumpEnactResult smbSetByPump = null; public String source = null; public Date lastAPSRun = null; - public Date lastEnact = null; - public Date lastOpenModeAccept; + public long lastTBREnact = 0; + public long lastSMBEnact = 0; + public long lastTBRRequest = 0; + public long lastSMBRequest = 0; + public long lastOpenModeAccept; } static public LastRun lastRun = null; @@ -343,6 +348,10 @@ public class LoopPlugin extends PluginBase { lastRun.source = ((PluginBase) usedAPS).getName(); lastRun.tbrSetByPump = null; lastRun.smbSetByPump = null; + lastRun.lastTBREnact = 0; + lastRun.lastTBRRequest = 0; + lastRun.lastSMBEnact = 0; + lastRun.lastSMBRequest = 0; NSUpload.uploadDeviceStatus(); @@ -379,14 +388,17 @@ public class LoopPlugin extends PluginBase { public void run() { if (result.enacted || result.success) { lastRun.tbrSetByPump = result; - lastRun.lastEnact = lastRun.lastAPSRun; + lastRun.lastTBRRequest = lastRun.lastAPSRun.getTime(); + lastRun.lastTBREnact = DateUtil.now(); + RxBus.INSTANCE.send(new EventLoopUpdateGui()); applySMBRequest(resultAfterConstraints, new Callback() { @Override public void run() { //Callback is only called if a bolus was acutally requested if (result.enacted || result.success) { lastRun.smbSetByPump = result; - lastRun.lastEnact = lastRun.lastAPSRun; + lastRun.lastSMBRequest = lastRun.lastAPSRun.getTime(); + lastRun.lastSMBEnact = DateUtil.now(); } else { new Thread(() -> { SystemClock.sleep(1000); @@ -466,8 +478,9 @@ public class LoopPlugin extends PluginBase { public void run() { if (result.enacted) { lastRun.tbrSetByPump = result; - lastRun.lastEnact = new Date(); - lastRun.lastOpenModeAccept = new Date(); + lastRun.lastTBRRequest = lastRun.lastAPSRun.getTime(); + lastRun.lastTBREnact = DateUtil.now(); + lastRun.lastOpenModeAccept = DateUtil.now(); NSUpload.uploadDeviceStatus(); SP.incInt(R.string.key_ObjectivesmanualEnacts); } @@ -686,7 +699,7 @@ public class LoopPlugin extends PluginBase { } }); } - NSUpload.uploadOpenAPSOffline(durationInMinutes); + createOfflineEvent(durationInMinutes); } public void suspendLoop(int durationInMinutes) { @@ -704,7 +717,23 @@ public class LoopPlugin extends PluginBase { } } }); - NSUpload.uploadOpenAPSOffline(durationInMinutes); + createOfflineEvent(durationInMinutes); } + public void createOfflineEvent(int durationInMinutes) { + JSONObject data = new JSONObject(); + try { + data.put("eventType", CareportalEvent.OPENAPSOFFLINE); + data.put("duration", durationInMinutes); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + CareportalEvent event = new CareportalEvent(); + event.date = DateUtil.now(); + event.source = Source.USER; + event.eventType = CareportalEvent.OPENAPSOFFLINE; + event.json = data.toString(); + MainApp.getDbHelper().createOrUpdate(event); + NSUpload.uploadOpenAPSOffline(event); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java index 703dc4255b..9ea2d39567 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java @@ -23,6 +23,7 @@ import javax.annotation.Nullable; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; @@ -194,8 +195,6 @@ public class DetermineBasalAdapterAMAJS { double autosensDataRatio, boolean tempTargetSet) throws JSONException { - String units = profile.getUnits(); - mProfile = new JSONObject(); mProfile.put("max_iob", maxIob); mProfile.put("dia", Math.min(profile.getDia(), 3d)); @@ -206,7 +205,7 @@ public class DetermineBasalAdapterAMAJS { mProfile.put("max_bg", maxBg); mProfile.put("target_bg", targetBg); mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units)); + mProfile.put("sens", profile.getIsfMgdl()); mProfile.put("max_daily_safety_multiplier", SP.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)); mProfile.put("current_basal_safety_multiplier", SP.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d)); mProfile.put("skip_neutral_temps", true); @@ -220,7 +219,7 @@ public class DetermineBasalAdapterAMAJS { mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)); } - if (units.equals(Constants.MMOL)) { + if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) { mProfile.put("out_units", "mmol/L"); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java index ef31ad599e..49881c5158 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java @@ -107,6 +107,13 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { return; } + if (pump == null) { + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.nopumpselected))); + if (L.isEnabled(L.APS)) + log.debug(MainApp.gs(R.string.nopumpselected)); + return; + } + if (!isEnabled(PluginType.APS)) { RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); if (L.isEnabled(L.APS)) @@ -121,12 +128,10 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { return; } - String units = profile.getUnits(); - double maxBasal = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value(); - double minBg = Profile.toMgdl(profile.getTargetLow(), units); - double maxBg = Profile.toMgdl(profile.getTargetHigh(), units); - double targetBg = Profile.toMgdl(profile.getTarget(), units); + double minBg = profile.getTargetLowMgdl(); + double maxBg = profile.getTargetHighMgdl(); + double targetBg = profile.getTargetMgdl(); minBg = Round.roundTo(minBg, 0.1d); maxBg = Round.roundTo(maxBg, 0.1d); @@ -162,9 +167,9 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { return; if (!HardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) return; - if (!HardLimits.checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) + if (!HardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; - if (!HardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) + if (!HardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal())) return; if (!HardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal())) return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java index 1ded34b7cb..dbc7782ab3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java @@ -20,6 +20,7 @@ import javax.annotation.Nullable; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; @@ -160,8 +161,6 @@ public class DetermineBasalAdapterMAJS { GlucoseStatus glucoseStatus, MealData mealData) throws JSONException { - String units = profile.getUnits(); - mProfile = new JSONObject(); mProfile.put("max_iob", maxIob); mProfile.put("dia", Math.min(profile.getDia(), 3d)); @@ -172,11 +171,11 @@ public class DetermineBasalAdapterMAJS { mProfile.put("max_bg", maxBg); mProfile.put("target_bg", targetBg); mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units)); + mProfile.put("sens", profile.getIsfMgdl()); mProfile.put("current_basal", basalRate); - if (units.equals(Constants.MMOL)) { + if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) { mProfile.put("out_units", "mmol/L"); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java index 899c0151d8..0380156c74 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java @@ -106,6 +106,13 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { return; } + if (pump == null) { + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.nopumpselected))); + if (L.isEnabled(L.APS)) + log.debug(MainApp.gs(R.string.nopumpselected)); + return; + } + if (!isEnabled(PluginType.APS)) { RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); if (L.isEnabled(L.APS)) @@ -120,13 +127,11 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { return; } - String units = profile.getUnits(); - double maxBasal = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value(); - double minBg = Profile.toMgdl(profile.getTargetLow(), units); - double maxBg = Profile.toMgdl(profile.getTargetHigh(), units); - double targetBg = Profile.toMgdl(profile.getTarget(), units); + double minBg = profile.getTargetLowMgdl(); + double maxBg = profile.getTargetHighMgdl(); + double targetBg = profile.getTargetMgdl(); minBg = Round.roundTo(minBg, 0.1d); maxBg = Round.roundTo(maxBg, 0.1d); @@ -160,9 +165,9 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { return; if (!checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) return; - if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) + if (!checkOnlyHardLimits(profile.getIsfMgdl(), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; - if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) + if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal())) return; if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal())) return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java index b7b6d82e5e..76571176d3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java @@ -23,6 +23,7 @@ import javax.annotation.Nullable; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; @@ -219,8 +220,6 @@ public class DetermineBasalAdapterSMBJS { boolean advancedFiltering ) throws JSONException { - String units = profile.getUnits(); - mProfile = new JSONObject(); mProfile.put("max_iob", maxIob); @@ -232,7 +231,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("max_bg", maxBg); mProfile.put("target_bg", targetBg); mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units)); + mProfile.put("sens", profile.getIsfMgdl()); mProfile.put("max_daily_safety_multiplier", SP.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)); mProfile.put("current_basal_safety_multiplier", SP.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d)); @@ -273,7 +272,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("temptargetSet", tempTargetSet); mProfile.put("autosens_max", SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); - if (units.equals(Constants.MMOL)) { + if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) { mProfile.put("out_units", "mmol/L"); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java index d410eba291..426c04090b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java @@ -111,6 +111,13 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr return; } + if (pump == null) { + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.nopumpselected))); + if (L.isEnabled(L.APS)) + log.debug(MainApp.gs(R.string.nopumpselected)); + return; + } + if (!isEnabled(PluginType.APS)) { RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); if (L.isEnabled(L.APS)) @@ -125,16 +132,14 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr return; } - String units = profile.getUnits(); - Constraint inputConstraints = new Constraint<>(0d); // fake. only for collecting all results Constraint maxBasalConstraint = MainApp.getConstraintChecker().getMaxBasalAllowed(profile); inputConstraints.copyReasons(maxBasalConstraint); double maxBasal = maxBasalConstraint.value(); - double minBg = Profile.toMgdl(profile.getTargetLow(), units); - double maxBg = Profile.toMgdl(profile.getTargetHigh(), units); - double targetBg = Profile.toMgdl(profile.getTarget(), units); + double minBg = profile.getTargetLowMgdl(); + double maxBg = profile.getTargetHighMgdl(); + double targetBg = profile.getTargetMgdl(); minBg = Round.roundTo(minBg, 0.1d); maxBg = Round.roundTo(maxBg, 0.1d); @@ -168,9 +173,9 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr return; if (!checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC)) return; - if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF)) + if (!checkOnlyHardLimits(profile.getIsfMgdl(), "sens", HardLimits.MINISF, HardLimits.MAXISF)) return; - if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal())) + if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal())) return; if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal())) return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt index 86e0f39437..2670e78633 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt @@ -2,7 +2,11 @@ package info.nightscout.androidaps.plugins.configBuilder import android.content.Intent import android.view.View -import android.widget.* +import android.widget.CheckBox +import android.widget.ImageButton +import android.widget.LinearLayout +import android.widget.RadioButton +import android.widget.TextView import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.events.EventRebuildTabs @@ -10,6 +14,7 @@ import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.utils.PasswordProtection +import info.nightscout.androidaps.utils.toVisibility class PluginViewHolder internal constructor(private val fragment: ConfigBuilderFragment, private val pluginType: PluginType, @@ -56,8 +61,8 @@ class PluginViewHolder internal constructor(private val fragment: ConfigBuilderF } fun update() { - enabledExclusive.visibility = if (areMultipleSelectionsAllowed(pluginType)) View.GONE else View.VISIBLE - enabledInclusive.visibility = if (areMultipleSelectionsAllowed(pluginType)) View.VISIBLE else View.GONE + enabledExclusive.visibility = areMultipleSelectionsAllowed(pluginType).not().toVisibility() + enabledInclusive.visibility = areMultipleSelectionsAllowed(pluginType).toVisibility() enabledExclusive.isChecked = plugin.isEnabled(pluginType) enabledInclusive.isChecked = plugin.isEnabled(pluginType) enabledInclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled @@ -70,7 +75,7 @@ class PluginViewHolder internal constructor(private val fragment: ConfigBuilderF pluginDescription.text = plugin.description } pluginPreferences.visibility = if (plugin.preferencesId == -1 || !plugin.isEnabled(pluginType)) View.INVISIBLE else View.VISIBLE - pluginVisibility.visibility = if (plugin.hasFragment()) View.VISIBLE else View.INVISIBLE + pluginVisibility.visibility = plugin.hasFragment().toVisibility() pluginVisibility.isEnabled = !(plugin.pluginDescription.neverVisible || plugin.pluginDescription.alwaysVisible) && plugin.isEnabled(pluginType) pluginVisibility.isChecked = plugin.isFragmentVisible } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java index 025d8dac1e..bdde6606bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java @@ -24,9 +24,10 @@ import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import io.reactivex.disposables.CompositeDisposable; @@ -75,35 +76,42 @@ public class ProfileFunctions { } public String getProfileName() { - return getProfileName(System.currentTimeMillis()); + return getProfileName(System.currentTimeMillis(), true, false); } public String getProfileName(boolean customized) { - return getProfileName(System.currentTimeMillis(), customized); + return getProfileName(System.currentTimeMillis(), customized, false); } - public String getProfileName(long time) { - return getProfileName(time, true); + public String getProfileNameWithDuration() { + return getProfileName(System.currentTimeMillis(), true, true); } - public String getProfileName(long time, boolean customized) { + public String getProfileName(long time, boolean customized, boolean showRemainingTime) { + String profileName = MainApp.gs(R.string.noprofileselected); + TreatmentsInterface activeTreatments = TreatmentsPlugin.getPlugin(); ProfileInterface activeProfile = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface(); ProfileSwitch profileSwitch = activeTreatments.getProfileSwitchFromHistory(time); if (profileSwitch != null) { if (profileSwitch.profileJson != null) { - return customized ? profileSwitch.getCustomizedName() : profileSwitch.profileName; + profileName = customized ? profileSwitch.getCustomizedName() : profileSwitch.profileName; } else { ProfileStore profileStore = activeProfile.getProfile(); if (profileStore != null) { Profile profile = profileStore.getSpecificProfile(profileSwitch.profileName); if (profile != null) - return profileSwitch.profileName; + profileName = profileSwitch.profileName; } } + + if (showRemainingTime && profileSwitch.durationInMinutes != 0) { + profileName += DateUtil.untilString(profileSwitch.originalEnd()); + } + return profileName; } - return MainApp.gs(R.string.noprofileselected); + return profileName; } public boolean isProfileValid(String from) { @@ -116,9 +124,8 @@ public class ProfileFunctions { return getProfile(System.currentTimeMillis()); } - public String getProfileUnits() { - Profile profile = getProfile(); - return profile != null ? profile.getUnits() : Constants.MGDL; + public static String getSystemUnits() { + return SP.getString(R.string.key_units, Constants.MGDL); } @Nullable @@ -163,8 +170,8 @@ public class ProfileFunctions { return profileSwitch; } - public static void doProfileSwitch(final ProfileStore profileStore, final String profileName, final int duration, final int percentage, final int timeshift) { - ProfileSwitch profileSwitch = prepareProfileSwitch(profileStore, profileName, duration, percentage, timeshift, System.currentTimeMillis()); + public static void doProfileSwitch(final ProfileStore profileStore, final String profileName, final int duration, final int percentage, final int timeshift, final long date) { + ProfileSwitch profileSwitch = prepareProfileSwitch(profileStore, profileName, duration, percentage, timeshift, date); TreatmentsPlugin.getPlugin().addToHistoryProfileSwitch(profileSwitch); if (percentage == 90 && duration == 10) SP.putBoolean(R.string.key_objectiveuseprofileswitch, true); @@ -176,7 +183,7 @@ public class ProfileFunctions { profileSwitch = new ProfileSwitch(); profileSwitch.date = System.currentTimeMillis(); profileSwitch.source = Source.USER; - profileSwitch.profileName = getInstance().getProfileName(System.currentTimeMillis(), false); + profileSwitch.profileName = getInstance().getProfileName(System.currentTimeMillis(), false, false); profileSwitch.profileJson = getInstance().getProfile().getData().toString(); profileSwitch.profilePlugin = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface().getClass().getName(); profileSwitch.durationInMinutes = duration; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java index e026d6589d..828fa1b86f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java @@ -22,7 +22,7 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific public class DstHelperPlugin extends PluginBase implements ConstraintsInterface { public static final int DISABLE_TIMEFRAME_HOURS = -3; - public static final int WARN_PRIOR_TIMEFRAME_HOURS = 24; + public static final int WARN_PRIOR_TIMEFRAME_HOURS = 12; private static Logger log = LoggerFactory.getLogger(L.CONSTRAINTS); static DstHelperPlugin plugin = null; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index 70242d3433..7f004b7f02 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -4,6 +4,7 @@ import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.Looper +import android.os.SystemClock import android.view.Gravity import android.view.LayoutInflater import android.view.View @@ -12,6 +13,7 @@ import android.widget.Button import android.widget.EditText import android.widget.LinearLayout import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller @@ -21,9 +23,12 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.logging.L import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog +import info.nightscout.androidaps.plugins.constraints.objectives.dialogs.NtpProgressDialog +import info.nightscout.androidaps.plugins.constraints.objectives.events.EventNtpStatus import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.ExamTask import info.nightscout.androidaps.receivers.NetworkChangeReceiver +import info.nightscout.androidaps.setupwizard.events.EventSWUpdate import info.nightscout.androidaps.utils.* import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable @@ -67,13 +72,13 @@ class ObjectivesFragment : Fragment() { override fun onResume() { super.onResume() disposable.add(RxBus - .toObservable(EventObjectivesUpdateGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - objectives_recyclerview.adapter?.notifyDataSetChanged() - }, { - FabricPrivacy.logException(it) - }) + .toObservable(EventObjectivesUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + objectives_recyclerview.adapter?.notifyDataSetChanged() + }, { + FabricPrivacy.logException(it) + }) ) } @@ -101,21 +106,20 @@ class ObjectivesFragment : Fragment() { } private fun scrollToCurrentObjective() { - for (i in 0 until ObjectivesPlugin.objectives.size) { - val objective = ObjectivesPlugin.objectives[i] - if (!objective.isStarted || !objective.isAccomplished) { - val smoothScroller = object : LinearSmoothScroller(context!!) { - override fun getVerticalSnapPreference(): Int { - return SNAP_TO_START - } - - override fun calculateTimeForScrolling(dx: Int): Int { - return super.calculateTimeForScrolling(dx) * 4 + activity?.runOnUiThread { + for (i in 0 until ObjectivesPlugin.objectives.size) { + val objective = ObjectivesPlugin.objectives[i] + if (!objective.isStarted || !objective.isAccomplished) { + context?.let { + val smoothScroller = object : LinearSmoothScroller(it) { + override fun getVerticalSnapPreference(): Int = SNAP_TO_START + override fun calculateTimeForScrolling(dx: Int): Int = super.calculateTimeForScrolling(dx) * 4 + } + smoothScroller.targetPosition = i + objectives_recyclerview.layoutManager?.startSmoothScroll(smoothScroller) } + break } - smoothScroller.targetPosition = i - objectives_recyclerview.layoutManager?.startSmoothScroll(smoothScroller) - break } } } @@ -129,7 +133,6 @@ class ObjectivesFragment : Fragment() { override fun onBindViewHolder(holder: ViewHolder, position: Int) { val objective = ObjectivesPlugin.objectives[position] holder.title.text = MainApp.gs(R.string.nth_objective, position + 1) - holder.revert.visibility = View.GONE if (objective.objective != 0) { holder.objective.visibility = View.VISIBLE holder.objective.text = MainApp.gs(objective.objective) @@ -145,6 +148,8 @@ class ObjectivesFragment : Fragment() { holder.verify.visibility = View.GONE holder.progress.visibility = View.GONE holder.accomplished.visibility = View.GONE + holder.unFinish.visibility = View.GONE + holder.unStart.visibility = View.GONE if (position == 0 || ObjectivesPlugin.objectives[position - 1].isAccomplished) holder.start.visibility = View.VISIBLE else @@ -155,15 +160,16 @@ class ObjectivesFragment : Fragment() { holder.progress.visibility = View.GONE holder.start.visibility = View.GONE holder.accomplished.visibility = View.VISIBLE + holder.unFinish.visibility = View.VISIBLE + holder.unStart.visibility = View.GONE } else if (objective.isStarted) { holder.gate.setTextColor(-0x1) holder.verify.visibility = View.VISIBLE holder.verify.isEnabled = objective.isCompleted || objectives_fake.isChecked holder.start.visibility = View.GONE holder.accomplished.visibility = View.GONE - if (objective.isRevertable) { - holder.revert.visibility = View.VISIBLE - } + holder.unFinish.visibility = View.GONE + holder.unStart.visibility = View.VISIBLE holder.progress.visibility = View.VISIBLE holder.progress.removeAllViews() for (task in objective.tasks) { @@ -206,76 +212,95 @@ class ObjectivesFragment : Fragment() { holder.accomplished.text = MainApp.gs(R.string.accomplished, DateUtil.dateAndTimeString(objective.accomplishedOn)) holder.accomplished.setTextColor(-0x3e3e3f) holder.verify.setOnClickListener { - holder.verify.visibility = View.INVISIBLE - NetworkChangeReceiver.fetch() + NetworkChangeReceiver.grabNetworkStatus(context) if (objectives_fake.isChecked) { objective.accomplishedOn = DateUtil.now() scrollToCurrentObjective() startUpdateTimer() RxBus.send(EventObjectivesUpdateGui()) - } else - SntpClient.ntpTime(object : SntpClient.Callback() { - override fun run() { - activity?.runOnUiThread { - holder.verify.visibility = View.VISIBLE + RxBus.send(EventSWUpdate(false)) + } else { + // move out of UI thread + Thread { + NtpProgressDialog().show((context as AppCompatActivity).supportFragmentManager, "NtpCheck") + RxBus.send(EventNtpStatus(MainApp.gs(R.string.timedetection), 0)) + SntpClient.ntpTime(object : SntpClient.Callback() { + override fun run() { log.debug("NTP time: $time System time: ${DateUtil.now()}") + SystemClock.sleep(300) if (!networkConnected) { - ToastUtils.showToastInUiThread(context, R.string.notconnected) + RxBus.send(EventNtpStatus(MainApp.gs(R.string.notconnected), 99)) } else if (success) { if (objective.isCompleted(time)) { objective.accomplishedOn = time - scrollToCurrentObjective() - startUpdateTimer() + RxBus.send(EventNtpStatus(MainApp.gs(R.string.success), 100)) + SystemClock.sleep(1000) RxBus.send(EventObjectivesUpdateGui()) + RxBus.send(EventSWUpdate(false)) + SystemClock.sleep(100) + scrollToCurrentObjective() } else { - ToastUtils.showToastInUiThread(context, R.string.requirementnotmet) + RxBus.send(EventNtpStatus(MainApp.gs(R.string.requirementnotmet), 99)) } } else { - ToastUtils.showToastInUiThread(context, R.string.failedretrievetime) + RxBus.send(EventNtpStatus(MainApp.gs(R.string.failedretrievetime), 99)) } } - } - }, NetworkChangeReceiver.isConnected()) + }, NetworkChangeReceiver.isConnected()) + }.start() + } } holder.start.setOnClickListener { - holder.start.visibility = View.INVISIBLE - NetworkChangeReceiver.fetch() + NetworkChangeReceiver.grabNetworkStatus(context) if (objectives_fake.isChecked) { objective.startedOn = DateUtil.now() scrollToCurrentObjective() startUpdateTimer() RxBus.send(EventObjectivesUpdateGui()) + RxBus.send(EventSWUpdate(false)) } else - SntpClient.ntpTime(object : SntpClient.Callback() { - override fun run() { - activity?.runOnUiThread { - holder.start.visibility = View.VISIBLE + // move out of UI thread + Thread { + NtpProgressDialog().show((context as AppCompatActivity).supportFragmentManager, "NtpCheck") + RxBus.send(EventNtpStatus(MainApp.gs(R.string.timedetection), 0)) + SntpClient.ntpTime(object : SntpClient.Callback() { + override fun run() { log.debug("NTP time: $time System time: ${DateUtil.now()}") + SystemClock.sleep(300) if (!networkConnected) { - ToastUtils.showToastInUiThread(context, R.string.notconnected) + RxBus.send(EventNtpStatus(MainApp.gs(R.string.notconnected), 99)) } else if (success) { objective.startedOn = time - scrollToCurrentObjective() - startUpdateTimer() + RxBus.send(EventNtpStatus(MainApp.gs(R.string.success), 100)) + SystemClock.sleep(1000) RxBus.send(EventObjectivesUpdateGui()) + RxBus.send(EventSWUpdate(false)) + SystemClock.sleep(100) + scrollToCurrentObjective() } else { - ToastUtils.showToastInUiThread(context, R.string.failedretrievetime) + RxBus.send(EventNtpStatus(MainApp.gs(R.string.failedretrievetime), 99)) } } - } - }, NetworkChangeReceiver.isConnected()) + }, NetworkChangeReceiver.isConnected()) + }.start() } - holder.revert.setOnClickListener { - objective.accomplishedOn = 0 - objective.startedOn = 0 - if (position > 0) { - val prevObj = ObjectivesPlugin.objectives[position - 1] - prevObj.accomplishedOn = 0 + holder.unStart.setOnClickListener { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.objectives), MainApp.gs(R.string.doyouwantresetstart), Runnable { + objective.startedOn = 0 + scrollToCurrentObjective() + RxBus.send(EventObjectivesUpdateGui()) + RxBus.send(EventSWUpdate(false)) + }) } + } + holder.unFinish.setOnClickListener { + objective.accomplishedOn = 0 scrollToCurrentObjective() RxBus.send(EventObjectivesUpdateGui()) + RxBus.send(EventSWUpdate(false)) } - if (objective.hasSpecialInput && !objective.isAccomplished && objective.isStarted) { + if (objective.hasSpecialInput && !objective.isAccomplished && objective.isStarted && objective.specialActionEnabled()) { // generate random request code if none exists val request = SP.getString(R.string.key_objectives_request_code, String.format("%1$05d", (Math.random() * 99999).toInt())) SP.putString(R.string.key_objectives_request_code, request) @@ -297,7 +322,6 @@ class ObjectivesFragment : Fragment() { } } - override fun getItemCount(): Int { return ObjectivesPlugin.objectives.size } @@ -310,7 +334,8 @@ class ObjectivesFragment : Fragment() { val progress: LinearLayout = itemView.findViewById(R.id.objective_progress) val verify: Button = itemView.findViewById(R.id.objective_verify) val start: Button = itemView.findViewById(R.id.objective_start) - val revert: Button = itemView.findViewById(R.id.objective_back) + val unFinish: Button = itemView.findViewById(R.id.objective_unfinish) + val unStart: Button = itemView.findViewById(R.id.objective_unstart) val inputHint: TextView = itemView.findViewById(R.id.objective_inputhint) val input: EditText = itemView.findViewById(R.id.objective_input) val enterButton: Button = itemView.findViewById(R.id.objective_enterbutton) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt index ca3e1d3126..ebae80150c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt @@ -104,8 +104,8 @@ object ObjectivesPlugin : PluginBase(PluginDescription() fun completeObjectives(activity: Activity, request: String) { val requestCode = SP.getString(R.string.key_objectives_request_code, "") var url = SP.getString(R.string.key_nsclientinternal_url, "").toLowerCase() - if (!url.endsWith("\"")) url = "$url/" - val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() + if (!url.endsWith("/")) url = "$url/" + @Suppress("DEPRECATION") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) { SP.putLong("Objectives_" + "openloop" + "_started", DateUtil.now()) SP.putLong("Objectives_" + "openloop" + "_accomplished", DateUtil.now()) @@ -122,9 +122,9 @@ object ObjectivesPlugin : PluginBase(PluginDescription() SP.putLong("Objectives_" + "smb" + "_started", DateUtil.now()) SP.putLong("Objectives_" + "smb" + "_accomplished", DateUtil.now()) setupObjectives() - OKDialog.show(activity, "", MainApp.gs(R.string.codeaccepted), null) + OKDialog.show(activity, MainApp.gs(R.string.objectives), MainApp.gs(R.string.codeaccepted)) } else { - OKDialog.show(activity, "", MainApp.gs(R.string.codeinvalid), null) + OKDialog.show(activity, MainApp.gs(R.string.objectives), MainApp.gs(R.string.codeinvalid)) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/dialogs/NtpProgressDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/dialogs/NtpProgressDialog.kt new file mode 100644 index 0000000000..77e6f22b43 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/dialogs/NtpProgressDialog.kt @@ -0,0 +1,85 @@ +package info.nightscout.androidaps.plugins.constraints.objectives.dialogs + +import android.os.Bundle +import android.os.SystemClock +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus.toObservable +import info.nightscout.androidaps.plugins.constraints.objectives.events.EventNtpStatus +import info.nightscout.androidaps.utils.FabricPrivacy +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.dialog_bolusprogress.* +import org.slf4j.LoggerFactory + +class NtpProgressDialog : DialogFragment() { + private val log = LoggerFactory.getLogger(L.UI) + private val disposable = CompositeDisposable() + + private val DEFAULT_STATE = MainApp.gs(R.string.timedetection) + private var state: String = DEFAULT_STATE + private var percent = 0 + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + dialog?.setTitle(String.format(MainApp.gs(R.string.objectives))) + isCancelable = false + + state = savedInstanceState?.getString("state", DEFAULT_STATE) ?: DEFAULT_STATE + percent = savedInstanceState?.getInt("percent", 0) ?: 0 + + return inflater.inflate(R.layout.dialog_bolusprogress, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + overview_bolusprogress_stop.setOnClickListener { dismiss() } + overview_bolusprogress_status.text = state + overview_bolusprogress_progressbar.max = 100 + overview_bolusprogress_progressbar.progress = percent + overview_bolusprogress_stop.text = MainApp.gs(R.string.close) + overview_bolusprogress_title.text = MainApp.gs(R.string.please_wait) + } + + override fun onResume() { + super.onResume() + if (L.isEnabled(L.UI)) log.debug("onResume") + if (percent == 100) { + dismiss() + return + } else + dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + + disposable.add(toObservable(EventNtpStatus::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ event: EventNtpStatus -> + if (L.isEnabled(L.UI)) log.debug("Status: " + event.status + " Percent: " + event.percent) + overview_bolusprogress_status?.text = event.status + overview_bolusprogress_progressbar?.progress = event.percent + if (event.percent == 100) { + SystemClock.sleep(100) + dismiss() + } + state = event.status + percent = event.percent + }) { FabricPrivacy.logException(it) } + ) + } + + override fun onPause() { + if (L.isEnabled(L.UI)) log.debug("onPause") + super.onPause() + disposable.clear() + } + + override fun onSaveInstanceState(outState: Bundle) { + outState.putString("state", state) + outState.putInt("percent", percent) + super.onSaveInstanceState(outState) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventNtpStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventNtpStatus.kt new file mode 100644 index 0000000000..fc4e5cb8e9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventNtpStatus.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.constraints.objectives.events + +import info.nightscout.androidaps.events.Event + +class EventNtpStatus(val status: String, val percent: Int) : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java index c882a921f5..7082c7e7bc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java @@ -61,10 +61,6 @@ public abstract class Objective { return true; } - public boolean isRevertable() { - return false; - } - public boolean isAccomplished() { return accomplishedOn != 0 && accomplishedOn < DateUtil.now(); } @@ -107,6 +103,8 @@ public abstract class Objective { return tasks; } + public boolean specialActionEnabled() { return true; } + public void specialAction(Activity activity, String input) {} public abstract class Task { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.java index c9ac7d8935..f8eb45bb2a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.java @@ -7,6 +7,7 @@ import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; @@ -38,6 +39,11 @@ public class Objective3 extends Objective { }); } + @Override + public boolean specialActionEnabled() { + return NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth; + } + @Override public void specialAction(Activity activity, String input) { ObjectivesPlugin.INSTANCE.completeObjectives(activity, input); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective5.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective5.java index 2407594069..bd3e69c751 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective5.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective5.java @@ -25,9 +25,4 @@ public class Objective5 extends Objective { } }); } - - @Override - public boolean isRevertable() { - return true; - } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt new file mode 100644 index 0000000000..7dbb642f45 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt @@ -0,0 +1,35 @@ +package info.nightscout.androidaps.plugins.constraints.phoneChecker + +import android.os.Build +import com.scottyab.rootbeer.RootBeer +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.ConstraintsInterface +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType + +object PhoneCheckerPlugin : PluginBase(PluginDescription() + .mainType(PluginType.CONSTRAINTS) + .neverVisible(true) + .alwaysEnabled(true) + .showInList(false) + .pluginName(R.string.phonechecker) +), ConstraintsInterface { + + var phoneRooted: Boolean = false + var devMode: Boolean = false + val phoneModel: String = Build.MODEL + val manufacturer: String = Build.MANUFACTURER + + private fun isDevModeEnabled(): Boolean { + return android.provider.Settings.Secure.getInt(MainApp.instance().contentResolver, + android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; + } + + override fun onStart() { + super.onStart() + phoneRooted = RootBeer(MainApp.instance()).isRootedWithoutBusyBoxCheck() + devMode = isDevModeEnabled() + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.java index 4d66f36194..5e7393d359 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.java @@ -21,6 +21,7 @@ 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; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.HardLimits; import info.nightscout.androidaps.utils.Round; @@ -73,7 +74,10 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { } value.set(false, MainApp.gs(R.string.closed_loop_disabled_on_dev_branch), this); } - + PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); + if (pump != null && !pump.isFakingTempsByExtendedBoluses() && TreatmentsPlugin.getPlugin().isInHistoryExtendedBoluslInProgress()) { + value.set(false, MainApp.gs(R.string.closed_loop_disabled_with_eb), this); + } return value; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.java index 89dbb56ca5..9c9e38bb46 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.java @@ -160,11 +160,12 @@ public class SignatureVerifierPlugin extends PluginBase implements ConstraintsIn return sb.toString(); } - private String singleCharUnMap(String shortHash) { + public String singleCharUnMap(String shortHash) { byte[] array = new byte[shortHash.length()]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; i++) { - sb.append(String.format("%02x",(int) map.charAt(map.indexOf(shortHash.charAt(i))))); + if (i != 0) sb.append(":"); + sb.append(String.format("%02X", 0xFF & map.charAt(map.indexOf(shortHash.charAt(i))))); } return sb.toString(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt index ee267c689e..9d88740d4b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt @@ -1,13 +1,19 @@ package info.nightscout.androidaps.plugins.constraints.versionChecker +import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.ConstraintsInterface +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.utils.SP import java.util.concurrent.TimeUnit +import kotlin.math.roundToInt /** * Usually we would have a class here. @@ -16,16 +22,23 @@ import java.util.concurrent.TimeUnit * */ object VersionCheckerPlugin : PluginBase(PluginDescription() - .mainType(PluginType.CONSTRAINTS) - .neverVisible(true) - .alwaysEnabled(true) - .showInList(false) - .pluginName(R.string.versionChecker)), ConstraintsInterface { + .mainType(PluginType.CONSTRAINTS) + .neverVisible(true) + .alwaysEnabled(true) + .showInList(false) + .pluginName(R.string.versionChecker)), ConstraintsInterface { + + private val gracePeriod: GracePeriod + get() = if ((BuildConfig.VERSION_NAME.contains("RC", ignoreCase = true))) { + GracePeriod.RC + } else { + GracePeriod.RELEASE + } override fun isClosedLoopAllowed(value: Constraint): Constraint { checkWarning() triggerCheckVersion() - return if (isOldVersion(GRACE_PERIOD_VERY_OLD)) + return if (isOldVersion(gracePeriod.veryOld.daysToMillis())) value.set(false, MainApp.gs(R.string.very_old_version), this) else value @@ -33,41 +46,49 @@ object VersionCheckerPlugin : PluginBase(PluginDescription() private fun checkWarning() { val now = System.currentTimeMillis() - + if (!SP.contains(R.string.key_last_versionchecker_plugin_warning)) { SP.putLong(R.string.key_last_versionchecker_plugin_warning, now) return } - if (isOldVersion(GRACE_PERIOD_WARNING) && shouldWarnAgain(now)) { + if (isOldVersion(gracePeriod.warning.daysToMillis()) && shouldWarnAgain(now)) { // store last notification time SP.putLong(R.string.key_last_versionchecker_plugin_warning, now) //notify - val message = MainApp.gs(R.string.new_version_warning, Math.round((now - SP.getLong(R.string.key_last_time_this_version_detected, now)) / TimeUnit.DAYS.toMillis(1).toDouble())) + val message = MainApp.gs(R.string.new_version_warning, + ((now - SP.getLong(R.string.key_last_time_this_version_detected, now)) / 1L.daysToMillis().toDouble()).roundToInt(), + gracePeriod.old, + gracePeriod.veryOld + ) val notification = Notification(Notification.OLDVERSION, message, Notification.NORMAL) RxBus.send(EventNewNotification(notification)) } } private fun shouldWarnAgain(now: Long) = - now > SP.getLong(R.string.key_last_versionchecker_plugin_warning, 0) + WARN_EVERY + now > SP.getLong(R.string.key_last_versionchecker_plugin_warning, 0) + WARN_EVERY override fun applyMaxIOBConstraints(maxIob: Constraint): Constraint = - if (isOldVersion(GRACE_PERIOD_OLD)) - maxIob.set(0.toDouble(), MainApp.gs(R.string.old_version), this) - else - maxIob + if (isOldVersion(gracePeriod.old.daysToMillis())) + maxIob.set(0.toDouble(), MainApp.gs(R.string.old_version), this) + else + maxIob private fun isOldVersion(gracePeriod: Long): Boolean { val now = System.currentTimeMillis() - return now > SP.getLong(R.string.key_last_time_this_version_detected, 0) + gracePeriod + return now > SP.getLong(R.string.key_last_time_this_version_detected, 0) + gracePeriod } - val WARN_EVERY = TimeUnit.DAYS.toMillis(1) - val GRACE_PERIOD_WARNING = TimeUnit.DAYS.toMillis(30) - val GRACE_PERIOD_OLD = TimeUnit.DAYS.toMillis(60) - val GRACE_PERIOD_VERY_OLD = TimeUnit.DAYS.toMillis(90) + private val WARN_EVERY = TimeUnit.DAYS.toMillis(1) } + +enum class GracePeriod(val warning: Long, val old: Long, val veryOld: Long) { + RELEASE(30, 60, 90), + RC(1, 7, 14) +} + +private fun Long.daysToMillis() = TimeUnit.DAYS.toMillis(this) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt index dfc06e6156..e68df84fdf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt @@ -7,6 +7,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import info.nightscout.androidaps.Config import info.nightscout.androidaps.MainApp @@ -18,20 +19,27 @@ import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction -import info.nightscout.androidaps.plugins.general.actions.dialogs.FillDialog -import info.nightscout.androidaps.plugins.general.actions.dialogs.NewExtendedBolusDialog -import info.nightscout.androidaps.plugins.general.actions.dialogs.NewTempBasalDialog +import info.nightscout.androidaps.dialogs.CareDialog +import info.nightscout.androidaps.dialogs.ExtendedBolusDialog +import info.nightscout.androidaps.dialogs.FillDialog +import info.nightscout.androidaps.dialogs.TempBasalDialog import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment -import info.nightscout.androidaps.plugins.general.careportal.Dialogs.NewNSTreatmentDialog +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.dialogs.ProfileSwitchDialog +import info.nightscout.androidaps.dialogs.TempTargetDialog +import info.nightscout.androidaps.logging.L import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.* import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.actions_fragment.* +import kotlinx.android.synthetic.main.careportal_stats_fragment.* +import org.slf4j.LoggerFactory import java.util.* class ActionsFragment : Fragment() { + private val log = LoggerFactory.getLogger(L.CORE) private var disposable: CompositeDisposable = CompositeDisposable() @@ -47,39 +55,52 @@ class ActionsFragment : Fragment() { super.onViewCreated(view, savedInstanceState) actions_profileswitch.setOnClickListener { - val newDialog = NewNSTreatmentDialog() - val profileSwitch = CareportalFragment.PROFILESWITCH - profileSwitch.executeProfileSwitch = true - newDialog.setOptions(profileSwitch, R.string.careportal_profileswitch) - fragmentManager?.let { newDialog.show(it, "NewNSTreatmentDialog") } + fragmentManager?.let { ProfileSwitchDialog().show(it, "Actions") } } actions_temptarget.setOnClickListener { - val newTTDialog = NewNSTreatmentDialog() - val temptarget = CareportalFragment.TEMPTARGET - temptarget.executeTempTarget = true - newTTDialog.setOptions(temptarget, R.string.careportal_temporarytarget) - fragmentManager?.let { newTTDialog.show(it, "NewNSTreatmentDialog") } + fragmentManager?.let { TempTargetDialog().show(it, "Actions") } } actions_extendedbolus.setOnClickListener { - fragmentManager?.let { NewExtendedBolusDialog().show(it, "NewExtendedDialog") } + context?.let { context -> + OKDialog.showConfirmation(context, MainApp.gs(R.string.extended_bolus), MainApp.gs(R.string.ebstopsloop), + Runnable { + fragmentManager?.let { ExtendedBolusDialog().show(it, "Actions") } + }, null) + } } actions_extendedbolus_cancel.setOnClickListener { if (TreatmentsPlugin.getPlugin().isInHistoryExtendedBoluslInProgress) { + log.debug("USER ENTRY: CANCEL EXTENDED BOLUS") ConfigBuilderPlugin.getPlugin().commandQueue.cancelExtended(object : Callback() { override fun run() { - if (!result.success) - ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.extendedbolusdeliveryerror)) + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.extendedbolusdeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } } }) } } - actions_settempbasal.setOnClickListener { fragmentManager?.let { NewTempBasalDialog().show(it, "NewTempDialog") } } + actions_settempbasal.setOnClickListener { + fragmentManager?.let { TempBasalDialog().show(it, "Actions") } + } actions_canceltempbasal.setOnClickListener { if (TreatmentsPlugin.getPlugin().isTempBasalInProgress) { + log.debug("USER ENTRY: CANCEL TEMP BASAL") ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { - if (!result.success) - ToastUtils.showToastInUiThread(MainApp.instance().applicationContext, MainApp.gs(R.string.tempbasaldeliveryerror)) + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } } }) } @@ -87,6 +108,21 @@ class ActionsFragment : Fragment() { actions_fill.setOnClickListener { fragmentManager?.let { FillDialog().show(it, "FillDialog") } } actions_historybrowser.setOnClickListener { startActivity(Intent(context, HistoryBrowseActivity::class.java)) } actions_tddstats.setOnClickListener { startActivity(Intent(context, TDDStatsActivity::class.java)) } + actions_bgcheck.setOnClickListener { + fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.BGCHECK, R.string.careportal_bgcheck).show(it, "Actions") } + } + actions_cgmsensorinsert.setOnClickListener { + fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.SENSOR_INSERT, R.string.careportal_cgmsensorinsert).show(it, "Actions") } + } + actions_pumpbatterychange.setOnClickListener { + fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.BATTERY_CHANGE, R.string.careportal_pumpbatterychange).show(it, "Actions") } + } + actions_note.setOnClickListener { + fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.NOTE, R.string.careportal_note).show(it, "Actions") } + } + actions_exercise.setOnClickListener { + fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.EXERCISE, R.string.careportal_exercise).show(it, "Actions") } + } SP.putBoolean(R.string.key_objectiveuseactions, true) } @@ -97,43 +133,27 @@ class ActionsFragment : Fragment() { disposable += RxBus .toObservable(EventInitializationChanged::class.java) .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateGui() - }, { - FabricPrivacy.logException(it) - }) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) disposable += RxBus .toObservable(EventRefreshOverview::class.java) .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateGui() - }, { - FabricPrivacy.logException(it) - }) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) disposable += RxBus .toObservable(EventExtendedBolusChange::class.java) .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateGui() - }, { - FabricPrivacy.logException(it) - }) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) disposable += RxBus .toObservable(EventTempBasalChange::class.java) .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateGui() - }, { - FabricPrivacy.logException(it) - }) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) disposable += RxBus .toObservable(EventCustomActionsChanged::class.java) .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateGui() - }, { - FabricPrivacy.logException(it) - }) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventCareportalEventChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) updateGui() } @@ -164,7 +184,7 @@ class ActionsFragment : Fragment() { actions_profileswitch?.visibility = if (!basalProfileEnabled || !pump.isInitialized || pump.isSuspended) View.GONE else View.VISIBLE - if (!pump.pumpDescription.isExtendedBolusCapable || !pump.isInitialized || pump.isSuspended || pump.isFakingTempsByExtendedBoluses || Config.APS) { + if (!pump.pumpDescription.isExtendedBolusCapable || !pump.isInitialized || pump.isSuspended || pump.isFakingTempsByExtendedBoluses) { actions_extendedbolus?.visibility = View.GONE actions_extendedbolus_cancel?.visibility = View.GONE } else { @@ -172,7 +192,7 @@ class ActionsFragment : Fragment() { if (activeExtendedBolus != null) { actions_extendedbolus?.visibility = View.GONE actions_extendedbolus_cancel?.visibility = View.VISIBLE - actions_extendedbolus_cancel?.text = MainApp.gs(R.string.cancel) + " " + activeExtendedBolus.toString() + actions_extendedbolus_cancel?.text = MainApp.gs(R.string.cancel) + " " + activeExtendedBolus.toStringMedium() } else { actions_extendedbolus?.visibility = View.VISIBLE actions_extendedbolus_cancel?.visibility = View.GONE @@ -198,8 +218,11 @@ class ActionsFragment : Fragment() { if (!pump.pumpDescription.isRefillingCapable || !pump.isInitialized || pump.isSuspended) View.GONE else View.VISIBLE - actions_temptarget?.visibility = if (!Config.APS) View.GONE else View.VISIBLE - actions_tddstats?.visibility = if (!pump.pumpDescription.supportsTDDs) View.GONE else View.VISIBLE + actions_temptarget?.visibility = Config.APS.toVisibility() + actions_tddstats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility() + activity?.let { activity -> + CareportalFragment.updateAge(activity, careportal_sensorage, careportal_insulinage, careportal_canulaage, careportal_pbage) + } checkPumpCustomActions() } @@ -224,8 +247,7 @@ class ActionsFragment : Fragment() { val action = this.pumpCustomActions[b.text.toString()] ConfigBuilderPlugin.getPlugin().activePump!!.executeCustomAction(action!!.customActionType) } - - val top = resources.getDrawable(customAction.iconResourceId) + val top = activity?.let { ContextCompat.getDrawable(it, customAction.iconResourceId) } btn.setCompoundDrawablesWithIntrinsicBounds(null, top, null, null) action_buttons_layout?.addView(btn) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsPlugin.kt index 04afb826a2..e534fefa26 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsPlugin.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.general.actions +import info.nightscout.androidaps.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription @@ -8,6 +9,8 @@ import info.nightscout.androidaps.interfaces.PluginType object ActionsPlugin : PluginBase(PluginDescription() .mainType(PluginType.GENERAL) .fragmentClass(ActionsFragment::class.qualifiedName) + .enableByDefault(Config.APS || Config.PUMPCONTROL) + .visibleByDefault(Config.APS || Config.PUMPCONTROL) .pluginName(R.string.actions) .shortName(R.string.actions_shortname) .description(R.string.description_actions)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomAction.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomAction.java deleted file mode 100644 index 5e5f707928..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomAction.java +++ /dev/null @@ -1,63 +0,0 @@ -package info.nightscout.androidaps.plugins.general.actions.defs; - -import info.nightscout.androidaps.R; - -/** - * Created by andy on 9/20/18. - */ - -public class CustomAction { - - private int name; - private String iconName; - private CustomActionType customActionType; - private int iconResourceId; - private boolean enabled = true; - - - public CustomAction(int nameResourceId, CustomActionType actionType) { - 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; - } - - - public int getIconResourceId() { - return iconResourceId; - } - - - public boolean isEnabled() { - return enabled; - } - - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomAction.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomAction.kt new file mode 100644 index 0000000000..fe9af4a03e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomAction.kt @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.plugins.general.actions.defs + +import info.nightscout.androidaps.R + +class CustomAction @JvmOverloads constructor(val name: Int, val customActionType: CustomActionType?, val iconResourceId: Int = R.drawable.icon_actions_profileswitch, var isEnabled: Boolean = true) { + + constructor(nameResourceId: Int, actionType: CustomActionType?, enabled: Boolean) : + this(nameResourceId, actionType, R.drawable.icon_actions_profileswitch, enabled) + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomActionType.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomActionType.java deleted file mode 100644 index 74ecf3c0e3..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomActionType.java +++ /dev/null @@ -1,11 +0,0 @@ -package info.nightscout.androidaps.plugins.general.actions.defs; - -/** - * Created by andy on 9/20/18. - */ - -public interface CustomActionType { - - String getKey(); - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomActionType.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomActionType.kt new file mode 100644 index 0000000000..fb472d5592 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/defs/CustomActionType.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.general.actions.defs + +interface CustomActionType { + val key: String? +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/FillDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/FillDialog.java deleted file mode 100644 index 77e4f85ea0..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/FillDialog.java +++ /dev/null @@ -1,255 +0,0 @@ -package info.nightscout.androidaps.plugins.general.actions.dialogs; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import androidx.fragment.app.DialogFragment; -import androidx.appcompat.app.AlertDialog; -import android.text.Editable; -import android.text.Html; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.LinearLayout; - -import com.google.common.base.Joiner; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.LinkedList; -import java.util.List; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.db.CareportalEvent; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.NumberPicker; -import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.utils.ToastUtils; - -import static info.nightscout.androidaps.utils.DateUtil.now; - -public class FillDialog extends DialogFragment implements OnClickListener { - private static Logger log = LoggerFactory.getLogger(FillDialog.class); - - private CheckBox pumpSiteChangeCheckbox; - private CheckBox insulinCartridgeChangeCheckbox; - - private NumberPicker editInsulin; - - double amount1 = 0d; - double amount2 = 0d; - double amount3 = 0d; - - private EditText notesEdit; - - //one shot guards - private boolean accepted; - private boolean okClicked; - - final private TextWatcher textWatcher = new TextWatcher() { - @Override - public void afterTextChanged(Editable s) { - validateInputs(); - } - - @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 void validateInputs() { - int time = editInsulin.getValue().intValue(); - if (Math.abs(time) > 12 * 60) { - editInsulin.setValue(0d); - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.constraintapllied)); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.actions_fill_dialog, container, false); - - view.findViewById(R.id.ok).setOnClickListener(this); - view.findViewById(R.id.cancel).setOnClickListener(this); - - getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); - getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - - pumpSiteChangeCheckbox = view.findViewById(R.id.fill_catheter_change); - insulinCartridgeChangeCheckbox = view.findViewById(R.id.fill_cartridge_change); - - 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, view.findViewById(R.id.ok), textWatcher); - - - Button preset1Button = view.findViewById(R.id.fill_preset_button1); - amount1 = SP.getDouble("fill_button1", 0.3); - if (amount1 > 0) { - preset1Button.setVisibility(View.VISIBLE); - preset1Button.setText(DecimalFormatter.toPumpSupportedBolus(amount1)); // + "U"); - preset1Button.setOnClickListener(this); - } else { - preset1Button.setVisibility(View.GONE); - } - Button preset2Button = view.findViewById(R.id.fill_preset_button2); - amount2 = SP.getDouble("fill_button2", 0d); - if (amount2 > 0) { - preset2Button.setVisibility(View.VISIBLE); - preset2Button.setText(DecimalFormatter.toPumpSupportedBolus(amount2)); // + "U"); - preset2Button.setOnClickListener(this); - } else { - preset2Button.setVisibility(View.GONE); - } - Button preset3Button = view.findViewById(R.id.fill_preset_button3); - amount3 = SP.getDouble("fill_button3", 0d); - if (amount3 > 0) { - preset3Button.setVisibility(View.VISIBLE); - preset3Button.setText(DecimalFormatter.toPumpSupportedBolus(amount3)); // + "U"); - preset3Button.setOnClickListener(this); - } else { - preset3Button.setVisibility(View.GONE); - } - - LinearLayout notesLayout = view.findViewById(R.id.fill_notes_layout); - notesLayout.setVisibility(SP.getBoolean(R.string.key_show_notes_entry_dialogs, false) ? View.VISIBLE : View.GONE); - notesEdit = view.findViewById(R.id.fill_notes); - - setCancelable(true); - getDialog().setCanceledOnTouchOutside(false); - return view; - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - confirmAndDeliver(); - break; - case R.id.cancel: - dismiss(); - break; - case R.id.fill_preset_button1: - editInsulin.setValue(amount1); - break; - case R.id.fill_preset_button2: - editInsulin.setValue(amount2); - break; - case R.id.fill_preset_button3: - editInsulin.setValue(amount3); - break; - } - - } - - private synchronized void confirmAndDeliver() { - if (okClicked) { - log.debug("guarding: ok already clicked"); - dismiss(); - return; - } - okClicked = true; - - try { - Double insulin = SafeParse.stringToDouble(editInsulin.getText()); - - List confirmMessage = new LinkedList<>(); - - Double insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(insulin)).value(); - if (insulinAfterConstraints > 0) { - confirmMessage.add(MainApp.gs(R.string.fillwarning)); - confirmMessage.add(""); - confirmMessage.add(MainApp.gs(R.string.bolus) + ": " + "" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + ""); - if (Math.abs(insulinAfterConstraints - insulin) > 0.01d) - confirmMessage.add(MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints)); - } - - if (pumpSiteChangeCheckbox.isChecked()) - confirmMessage.add("" + "" + MainApp.gs(R.string.record_pump_site_change) + ""); - - if (insulinCartridgeChangeCheckbox.isChecked()) - confirmMessage.add("" + "" + MainApp.gs(R.string.record_insulin_cartridge_change) + ""); - - final String notes = notesEdit.getText().toString(); - if (!notes.isEmpty()) { - confirmMessage.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes); - } - - final Double finalInsulinAfterConstraints = insulinAfterConstraints; - - final Context context = getContext(); - AlertDialog.Builder builder = new AlertDialog.Builder(context); - - builder.setTitle(MainApp.gs(R.string.confirmation)); - if (insulinAfterConstraints > 0 || pumpSiteChangeCheckbox.isChecked() || insulinCartridgeChangeCheckbox.isChecked()) { - builder.setMessage(Html.fromHtml(Joiner.on("
").join(confirmMessage))); - builder.setPositiveButton(MainApp.gs(R.string.primefill), (dialog, id) -> { - synchronized (builder) { - if (accepted) { - log.debug("guarding: already accepted"); - return; - } - accepted = true; - - if (finalInsulinAfterConstraints > 0) { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.insulin = finalInsulinAfterConstraints; - detailedBolusInfo.context = context; - detailedBolusInfo.source = Source.USER; - detailedBolusInfo.isValid = false; // do not count it in IOB (for pump history) - detailedBolusInfo.notes = notes; - ConfigBuilderPlugin.getPlugin().getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } - } - }); - } - if (pumpSiteChangeCheckbox.isChecked()) - NSUpload.uploadEvent(CareportalEvent.SITECHANGE, now(), notes); - if (insulinCartridgeChangeCheckbox.isChecked()) - NSUpload.uploadEvent(CareportalEvent.INSULINCHANGE, now() + 1000, notes); - } - }); - } else { - builder.setMessage(MainApp.gs(R.string.no_action_selected)); - } - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - dismiss(); - } catch (RuntimeException e) { - log.error("Unhandled exception", e); - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/NewExtendedBolusDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/NewExtendedBolusDialog.java deleted file mode 100644 index 99e877efcc..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/NewExtendedBolusDialog.java +++ /dev/null @@ -1,115 +0,0 @@ -package info.nightscout.androidaps.plugins.general.actions.dialogs; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import androidx.fragment.app.DialogFragment; -import androidx.appcompat.app.AlertDialog; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -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.interfaces.Constraint; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.utils.NumberPicker; -import info.nightscout.androidaps.utils.SafeParse; - -public class NewExtendedBolusDialog extends DialogFragment implements View.OnClickListener { - private static Logger log = LoggerFactory.getLogger(NewExtendedBolusDialog.class); - - NumberPicker editInsulin; - NumberPicker editDuration; - - public NewExtendedBolusDialog() { - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - getDialog().setTitle(MainApp.gs(R.string.overview_extendedbolus_button)); - - View view = inflater.inflate(R.layout.overview_newextendedbolus_dialog, container, false); - - 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, 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, view.findViewById(R.id.ok)); - - view.findViewById(R.id.ok).setOnClickListener(this); - view.findViewById(R.id.cancel).setOnClickListener(this); - - setCancelable(true); - getDialog().setCanceledOnTouchOutside(false); - return view; - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - try { - Double insulin = SafeParse.stringToDouble(editInsulin.getText()); - int durationInMinutes = SafeParse.stringToInt(editDuration.getText()); - - String confirmMessage = MainApp.gs(R.string.setextendedbolusquestion); - - Double insulinAfterConstraint = MainApp.getConstraintChecker().applyExtendedBolusConstraints(new Constraint<>(insulin)).value(); - confirmMessage += " " + insulinAfterConstraint + " U "; - confirmMessage += MainApp.gs(R.string.duration) + " " + durationInMinutes + "min ?"; - if (Math.abs(insulinAfterConstraint - insulin) > 0.01d) - confirmMessage += "\n" + MainApp.gs(R.string.constraintapllied); - insulin = insulinAfterConstraint; - - final Double finalInsulin = insulin; - final int finalDurationInMinutes = durationInMinutes; - - final Context context = getContext(); - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(confirmMessage); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - ConfigBuilderPlugin.getPlugin().getCommandQueue().extendedBolus(finalInsulin, finalDurationInMinutes, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } - } - }); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - dismiss(); - - } catch (Exception e) { - log.error("Unhandled exception", e); - } - break; - case R.id.cancel: - dismiss(); - break; - } - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/NewTempBasalDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/NewTempBasalDialog.java deleted file mode 100644 index cad2e22f85..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/dialogs/NewTempBasalDialog.java +++ /dev/null @@ -1,196 +0,0 @@ -package info.nightscout.androidaps.plugins.general.actions.dialogs; - -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import androidx.fragment.app.DialogFragment; -import androidx.appcompat.app.AlertDialog; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.RadioButton; -import android.widget.RadioGroup; - -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.data.Profile; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.PumpDescription; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.utils.NumberPicker; -import info.nightscout.androidaps.utils.SafeParse; - -public class NewTempBasalDialog extends DialogFragment implements View.OnClickListener, RadioGroup.OnCheckedChangeListener { - private static Logger log = LoggerFactory.getLogger(NewTempBasalDialog.class); - - RadioButton percentRadio; - RadioButton absoluteRadio; - RadioGroup basalTypeRadioGroup; - LinearLayout typeSelectorLayout; - - LinearLayout percentLayout; - LinearLayout absoluteLayout; - - NumberPicker basalPercent; - NumberPicker basalAbsolute; - NumberPicker duration; - - public NewTempBasalDialog() { - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - getDialog().setTitle(MainApp.gs(R.string.overview_tempbasal_button)); - - View view = inflater.inflate(R.layout.overview_newtempbasal_dialog, container, false); - - percentLayout = (LinearLayout) view.findViewById(R.id.overview_newtempbasal_percent_layout); - absoluteLayout = (LinearLayout) view.findViewById(R.id.overview_newtempbasal_absolute_layout); - percentRadio = (RadioButton) view.findViewById(R.id.overview_newtempbasal_percent_radio); - basalTypeRadioGroup = (RadioGroup) view.findViewById(R.id.overview_newtempbasal_radiogroup); - absoluteRadio = (RadioButton) view.findViewById(R.id.overview_newtempbasal_absolute_radio); - typeSelectorLayout = (LinearLayout) view.findViewById(R.id.overview_newtempbasal_typeselector_layout); - - PumpDescription pumpDescription = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription(); - - 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, 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, 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, view.findViewById(R.id.ok)); - - if ((pumpDescription.tempBasalStyle & PumpDescription.PERCENT) == PumpDescription.PERCENT && (pumpDescription.tempBasalStyle & PumpDescription.ABSOLUTE) == PumpDescription.ABSOLUTE) { - // Both allowed - typeSelectorLayout.setVisibility(View.VISIBLE); - } else { - typeSelectorLayout.setVisibility(View.GONE); - } - - if ((pumpDescription.tempBasalStyle & PumpDescription.PERCENT) == PumpDescription.PERCENT) { - percentRadio.setChecked(true); - absoluteRadio.setChecked(false); - percentLayout.setVisibility(View.VISIBLE); - absoluteLayout.setVisibility(View.GONE); - } else if ((pumpDescription.tempBasalStyle & PumpDescription.ABSOLUTE) == PumpDescription.ABSOLUTE) { - percentRadio.setChecked(false); - absoluteRadio.setChecked(true); - percentLayout.setVisibility(View.GONE); - absoluteLayout.setVisibility(View.VISIBLE); - } - - view.findViewById(R.id.ok).setOnClickListener(this); - view.findViewById(R.id.cancel).setOnClickListener(this); - basalTypeRadioGroup.setOnCheckedChangeListener(this); - - setCancelable(true); - getDialog().setCanceledOnTouchOutside(false); - return view; - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - try { - int percent = 0; - Double absolute = 0d; - final boolean setAsPercent = percentRadio.isChecked(); - int durationInMinutes = SafeParse.stringToInt(duration.getText()); - - Profile profile = ProfileFunctions.getInstance().getProfile(); - if (profile == null) - return; - - String confirmMessage = MainApp.gs(R.string.setbasalquestion); - if (setAsPercent) { - int basalPercentInput = SafeParse.stringToInt(basalPercent.getText()); - percent = MainApp.getConstraintChecker().applyBasalPercentConstraints(new Constraint<>(basalPercentInput), profile).value(); - confirmMessage += "\n" + percent + "% "; - confirmMessage += "\n" + MainApp.gs(R.string.duration) + " " + durationInMinutes + "min ?"; - if (percent != basalPercentInput) - confirmMessage += "\n" + MainApp.gs(R.string.constraintapllied); - } else { - Double basalAbsoluteInput = SafeParse.stringToDouble(basalAbsolute.getText()); - absolute = MainApp.getConstraintChecker().applyBasalConstraints(new Constraint<>(basalAbsoluteInput), profile).value(); - confirmMessage += "\n" + absolute + " U/h "; - confirmMessage += "\n" + MainApp.gs(R.string.duration) + " " + durationInMinutes + "min ?"; - if (Math.abs(absolute - basalAbsoluteInput) > 0.01d) - confirmMessage += "\n" + MainApp.gs(R.string.constraintapllied); - } - - final int finalBasalPercent = percent; - final Double finalBasal = absolute; - final int finalDurationInMinutes = durationInMinutes; - - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(confirmMessage); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - Callback callback = new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } - } - }; - if (setAsPercent) { - ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalPercent(finalBasalPercent, finalDurationInMinutes, true, profile, callback); - } else { - ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalAbsolute(finalBasal, finalDurationInMinutes, true, profile, callback); - } - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - dismiss(); - - } catch (Exception e) { - log.error("Unhandled exception", e); - } - break; - case R.id.cancel: - dismiss(); - break; - } - } - - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - switch (checkedId) { - case R.id.overview_newtempbasal_percent_radio: - percentLayout.setVisibility(View.VISIBLE); - absoluteLayout.setVisibility(View.GONE); - break; - case R.id.overview_newtempbasal_absolute_radio: - percentLayout.setVisibility(View.GONE); - absoluteLayout.setVisibility(View.VISIBLE); - break; - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java index 6eb3be382b..4f7bb62c74 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java @@ -10,8 +10,11 @@ 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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AutomationEvent { + private static final Logger log = LoggerFactory.getLogger(AutomationEvent.class); private Trigger trigger = new TriggerConnector(); private List actions = new ArrayList<>(); @@ -74,7 +77,7 @@ public class AutomationEvent { } o.put("actions", array); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -91,7 +94,7 @@ public class AutomationEvent { actions.add(Action.instantiate(new JSONObject(array.getString(i)))); } } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt index d1135c21ed..1db87f6472 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt @@ -1,27 +1,35 @@ package info.nightscout.androidaps.plugins.general.automation import android.os.Bundle +import android.text.method.ScrollingMovementMethod import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView 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.dragHelpers.OnStartDragListener +import info.nightscout.androidaps.plugins.general.automation.dragHelpers.SimpleItemTouchHelperCallback import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.plusAssign import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.automation_fragment.* -class AutomationFragment : Fragment() { +class AutomationFragment : Fragment(), OnStartDragListener { private var disposable: CompositeDisposable = CompositeDisposable() private var eventListAdapter: EventListAdapter? = null + private var itemTouchHelper: ItemTouchHelper? = null + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.automation_fragment, container, false) } @@ -29,10 +37,12 @@ class AutomationFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - eventListAdapter = EventListAdapter(AutomationPlugin.automationEvents, fragmentManager, activity) + eventListAdapter = EventListAdapter(AutomationPlugin.automationEvents, fragmentManager, activity, this) automation_eventListView.layoutManager = LinearLayoutManager(context) automation_eventListView.adapter = eventListAdapter + automation_logView.setMovementMethod(ScrollingMovementMethod()) + automation_fabAddEvent.setOnClickListener { val dialog = EditEventDialog() val args = Bundle() @@ -42,6 +52,10 @@ class AutomationFragment : Fragment() { fragmentManager?.let { dialog.show(it, "EditEventDialog") } } + val callback: ItemTouchHelper.Callback = SimpleItemTouchHelperCallback(eventListAdapter!!) + itemTouchHelper = ItemTouchHelper(callback) + itemTouchHelper?.attachToRecyclerView(automation_eventListView) + } @Synchronized @@ -77,8 +91,12 @@ class AutomationFragment : Fragment() { eventListAdapter?.notifyDataSetChanged() val sb = StringBuilder() for (l in AutomationPlugin.executionLog.reversed()) - sb.append(l).append("\n") - automation_logView?.text = sb.toString() + sb.append(l).append("
") + automation_logView?.text = HtmlHelper.fromHtml(sb.toString()) + } + + override fun onStartDrag(viewHolder: RecyclerView.ViewHolder) { + itemTouchHelper?.startDrag(viewHolder); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt index 8b78ae6458..0d220c2dd4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt @@ -161,7 +161,7 @@ object AutomationPlugin : PluginBase(PluginDescription() private fun processActions() { if (!isEnabled(PluginType.GENERAL)) return - if (LoopPlugin.getPlugin().isSuspended) { + if (LoopPlugin.getPlugin().isSuspended || !LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) { if (L.isEnabled(L.AUTOMATION)) log.debug("Loop deactivated") return @@ -178,10 +178,10 @@ object AutomationPlugin : PluginBase(PluginDescription() val sb = StringBuilder() sb.append(DateUtil.timeString(DateUtil.now())) sb.append(" ") - sb.append(if (result.success) "☺" else "X") - sb.append(" ") + sb.append(if (result.success) "☺" else "▼") + sb.append(" ") sb.append(event.title) - sb.append(": ") + sb.append(": ") sb.append(action.shortDescription()) sb.append(": ") sb.append(result.comment) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/EventListAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/EventListAdapter.java index 08079a9110..87c78d02eb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/EventListAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/EventListAdapter.java @@ -1,9 +1,12 @@ package info.nightscout.androidaps.plugins.general.automation; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; +import android.graphics.Color; import android.os.Bundle; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; @@ -17,6 +20,7 @@ import androidx.annotation.NonNull; import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.RecyclerView; +import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -25,19 +29,26 @@ 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.dialogs.EditEventDialog; +import info.nightscout.androidaps.plugins.general.automation.dragHelpers.ItemTouchHelperAdapter; +import info.nightscout.androidaps.plugins.general.automation.dragHelpers.ItemTouchHelperViewHolder; +import info.nightscout.androidaps.plugins.general.automation.dragHelpers.OnStartDragListener; 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.TriggerConnector; import info.nightscout.androidaps.utils.OKDialog; -class EventListAdapter extends RecyclerView.Adapter { +class EventListAdapter extends RecyclerView.Adapter implements ItemTouchHelperAdapter { private final List eventList; private final FragmentManager fragmentManager; private final Activity activity; - EventListAdapter(List events, FragmentManager fragmentManager, Activity activity) { + private final OnStartDragListener mDragStartListener; + + EventListAdapter(List events, FragmentManager fragmentManager, Activity activity, OnStartDragListener dragStartListener) { this.eventList = events; this.fragmentManager = fragmentManager; this.activity = activity; + mDragStartListener = dragStartListener; } @NonNull @@ -54,6 +65,7 @@ class EventListAdapter extends RecyclerView.Adapter layout.addView(iv); } + @SuppressLint("ClickableViewAccessibility") @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { final AutomationEvent event = eventList.get(position); @@ -91,16 +103,10 @@ class EventListAdapter extends RecyclerView.Adapter RxBus.INSTANCE.send(new EventAutomationDataChanged()); }); - // remove event - holder.iconTrash.setOnClickListener(v -> - OKDialog.showConfirmation(activity, MainApp.gs(R.string.removerecord) + " " + event.getTitle(), () -> { - eventList.remove(event); - RxBus.INSTANCE.send(new EventAutomationDataChanged()); - }) - ); - // edit event - holder.rootLayout.setOnClickListener(v -> { + holder.rootLayout.setOnClickListener(v -> + + { //EditEventDialog dialog = EditEventDialog.Companion.newInstance(event, false); EditEventDialog dialog = new EditEventDialog(); Bundle args = new Bundle(); @@ -110,6 +116,17 @@ class EventListAdapter extends RecyclerView.Adapter if (fragmentManager != null) dialog.show(fragmentManager, "EditEventDialog"); }); + + // Start a drag whenever the handle view it touched + holder.iconSort.setOnTouchListener((v, motionEvent) -> + + { + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + mDragStartListener.onStartDrag(holder); + return true; + } + return v.onTouchEvent(motionEvent); + }); } @Override @@ -117,12 +134,33 @@ class EventListAdapter extends RecyclerView.Adapter return eventList.size(); } - static class ViewHolder extends RecyclerView.ViewHolder { + @Override + public boolean onItemMove(int fromPosition, int toPosition) { + Collections.swap(eventList, fromPosition, toPosition); + notifyItemMoved(fromPosition, toPosition); + RxBus.INSTANCE.send(new EventAutomationDataChanged()); + return true; + } + + @Override + public void onItemDismiss(int position) { + OKDialog.showConfirmation(activity, MainApp.gs(R.string.removerecord) + " " + eventList.get(position).getTitle(), + () -> { + eventList.remove(position); + notifyItemRemoved(position); + RxBus.INSTANCE.send(new EventAutomationDataChanged()); + RxBus.INSTANCE.send(new EventAutomationUpdateGui()); + }, () -> { + RxBus.INSTANCE.send(new EventAutomationUpdateGui()); + }); + } + + static class ViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder { final RelativeLayout rootLayout; final LinearLayout iconLayout; final TextView eventTitle; final Context context; - final ImageView iconTrash; + final ImageView iconSort; final CheckBox enabled; ViewHolder(View view, Context context) { @@ -131,8 +169,18 @@ class EventListAdapter extends RecyclerView.Adapter eventTitle = view.findViewById(R.id.viewEventTitle); rootLayout = view.findViewById(R.id.rootLayout); iconLayout = view.findViewById(R.id.iconLayout); - iconTrash = view.findViewById(R.id.iconTrash); + iconSort = view.findViewById(R.id.iconSort); enabled = view.findViewById(R.id.automation_enabled); } + + @Override + public void onItemSelected() { + itemView.setBackgroundColor(Color.LTGRAY); + } + + @Override + public void onItemClear() { + itemView.setBackgroundColor(MainApp.gc(R.color.ribbonDefault)); + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java index 796f023f1d..ee4564fd6f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.java @@ -11,6 +11,8 @@ import javax.annotation.Nullable; import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger; import info.nightscout.androidaps.queue.Callback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /* Action ideas: @@ -44,6 +46,7 @@ import info.nightscout.androidaps.queue.Callback; public abstract class Action { + private static final Logger log = LoggerFactory.getLogger(Action.class); public Trigger precondition = null; @@ -65,7 +68,7 @@ public abstract class Action { try { o.put("type", this.getClass().getName()); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -84,7 +87,7 @@ public abstract class Action { Class clazz = Class.forName(type); return ((Action) clazz.newInstance()).fromJSON(data != null ? data.toString() : ""); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return null; } @@ -98,7 +101,7 @@ public abstract class Action { fromJSON(data.toString()); } } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResume.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResume.java index 38e5dee33a..16b478bbc2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResume.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResume.java @@ -28,7 +28,7 @@ public class ActionLoopResume extends Action { if (LoopPlugin.getPlugin().isSuspended()) { LoopPlugin.getPlugin().suspendTo(0); ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopResume"); - NSUpload.uploadOpenAPSOffline(0); + LoopPlugin.getPlugin().createOfflineEvent(0); RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopResume")); if (callback != null) callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java index bd221d8f81..39adbcac27 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.java @@ -18,8 +18,12 @@ import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithE import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.JsonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionLoopSuspend extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionLoopSuspend.class); + public InputDuration minutes = new InputDuration(0, InputDuration.TimeUnit.MINUTES); @Override @@ -59,7 +63,7 @@ public class ActionLoopSuspend extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -70,7 +74,7 @@ public class ActionLoopSuspend extends Action { JSONObject o = new JSONObject(data); minutes.setMinutes(JsonHelper.safeGetInt(o, "minutes")); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java index 1f7ad543c2..7bd501b460 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionNotification.java @@ -20,8 +20,12 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.JsonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionNotification extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionNotification.class); + public InputString text = new InputString(); @Override @@ -59,7 +63,7 @@ public class ActionNotification extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -70,7 +74,7 @@ public class ActionNotification extends Action { JSONObject o = new JSONObject(data); text.setValue(JsonHelper.safeGetString(o, "text")); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java index 50fbb786db..63c923fdcf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitch.java @@ -21,17 +21,29 @@ import info.nightscout.androidaps.plugins.general.automation.elements.InputProfi 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.DateUtil; import info.nightscout.androidaps.utils.JsonHelper; public class ActionProfileSwitch extends Action { private static Logger log = LoggerFactory.getLogger(L.AUTOMATION); - public InputProfileName inputProfileName = new InputProfileName(ProfileFunctions.getInstance().getProfileName()); + InputProfileName inputProfileName; String profileName = ""; public ActionProfileSwitch() { // Prevent action if active profile is already active // but we don't have a trigger IS_NOT_EQUAL // so check is in the doRun() + ProfileInterface profileInterface = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface(); + if (profileInterface != null) { + ProfileStore profileStore = profileInterface.getProfile(); + if (profileStore != null) { + String name = profileStore.getDefaultProfileName(); + if (name != null) { + profileName = name; + } + } + } + inputProfileName = new InputProfileName(profileName); } @Override @@ -50,23 +62,43 @@ public class ActionProfileSwitch extends Action { String activeProfileName = ProfileFunctions.getInstance().getProfileName(); //Check for uninitialized profileName - if ( profileName.equals("")){ profileName = activeProfileName; } - + if ( profileName.equals("")){ + log.error("Selected profile not initialized"); + if (callback != null) + callback.result(new PumpEnactResult().success(false).comment(R.string.error_field_must_not_be_empty)).run(); + return; + } + if ( ProfileFunctions.getInstance().getProfile() == null){ + log.error("ProfileFunctions not initialized"); + if (callback != null) + callback.result(new PumpEnactResult().success(false).comment(R.string.noprofile)).run(); + return; + } if (profileName.equals(activeProfileName)) { - // Profile is already switched + if (L.isEnabled(L.AUTOMATION)) + log.debug("Profile is already switched"); + if (callback != null) + callback.result(new PumpEnactResult().success(true).comment(R.string.alreadyset)).run(); return; } ProfileInterface activeProfile = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface(); - if (activeProfile == null) return; + if (activeProfile == null) { + log.error("ProfileInterface not initialized"); + if (callback != null) + callback.result(new PumpEnactResult().success(false).comment(R.string.noprofile)).run(); + return; + } ProfileStore profileStore = activeProfile.getProfile(); if (profileStore == null) return; if(profileStore.getSpecificProfile(profileName) == null) { if (L.isEnabled(L.AUTOMATION)) log.error("Selected profile does not exist! - "+ profileName); + if (callback != null) + callback.result(new PumpEnactResult().success(false).comment(R.string.notexists)).run(); return; } - ProfileFunctions.doProfileSwitch(profileStore, profileName, 0, 100, 0); + ProfileFunctions.doProfileSwitch(profileStore, profileName, 0, 100, 0, DateUtil.now()); if (callback != null) callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run(); } @@ -92,7 +124,7 @@ public class ActionProfileSwitch extends Action { data.put("profileToSwitchTo", inputProfileName.getValue()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -105,7 +137,7 @@ public class ActionProfileSwitch extends Action { profileName = JsonHelper.safeGetString(d, "profileToSwitchTo"); inputProfileName.setValue(profileName); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java index 20b6abb081..23b54640a5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionProfileSwitchPercent.java @@ -19,8 +19,12 @@ import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuil import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerProfilePercent; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.JsonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionProfileSwitchPercent extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionProfileSwitchPercent.class); + InputPercent pct = new InputPercent(); InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES); @@ -71,7 +75,7 @@ public class ActionProfileSwitchPercent extends Action { data.put("durationInMinutes", duration.getMinutes()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -83,7 +87,7 @@ public class ActionProfileSwitchPercent extends Action { pct.setValue(JsonHelper.safeGetInt(d, "percentage")); duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes")); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java index f8afafca75..fa9140e5f9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionSendSMS.java @@ -21,6 +21,7 @@ import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.JsonHelper; public class ActionSendSMS extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionSendSMS.class); public InputString text = new InputString(); @@ -36,7 +37,7 @@ public class ActionSendSMS extends Action { @Override public void doAction(Callback callback) { - boolean result = SmsCommunicatorPlugin.getPlugin().sendNotificationToAllNumbers(text.getValue()); + boolean result = SmsCommunicatorPlugin.INSTANCE.sendNotificationToAllNumbers(text.getValue()); if (callback != null) callback.result(new PumpEnactResult().success(result).comment(result ? R.string.ok : R.string.danar_error)).run(); @@ -56,7 +57,7 @@ public class ActionSendSMS extends Action { o.put("type", this.getClass().getName()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -67,7 +68,7 @@ public class ActionSendSMS extends Action { JSONObject o = new JSONObject(data); text.setValue(JsonHelper.safeGetString(o, "text")); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java index 659bb508eb..1d7ab8db48 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java @@ -24,8 +24,12 @@ 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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionStartTempTarget extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionStartTempTarget.class); + String reason = ""; InputTempTarget value = new InputTempTarget(); InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES); @@ -71,7 +75,7 @@ public class ActionStartTempTarget extends Action { 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_temporarytarget) + "\n[" + MainApp.gs(unitResId) + "]", "", value)) .add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", duration)) .build(root); } @@ -93,7 +97,7 @@ public class ActionStartTempTarget extends Action { data.put("durationInMinutes", duration.getMinutes()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -107,7 +111,7 @@ public class ActionStartTempTarget extends Action { value.setValue(JsonHelper.safeGetDouble(d, "value")); duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes")); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java index de251c499e..abf354bc9c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStopTempTarget.java @@ -14,8 +14,12 @@ 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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ActionStopTempTarget extends Action { + private static final Logger log = LoggerFactory.getLogger(ActionStopTempTarget.class); + String reason = ""; private TempTarget tempTarget; @@ -54,7 +58,7 @@ public class ActionStopTempTarget extends Action { data.put("reason", reason); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -65,7 +69,7 @@ public class ActionStopTempTarget extends Action { JSONObject d = new JSONObject(data); reason = JsonHelper.safeGetString(d, "reason"); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ActionListAdapter.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ActionListAdapter.kt index 8c9efedd20..d3a8eb20c0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ActionListAdapter.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ActionListAdapter.kt @@ -32,7 +32,7 @@ class ActionListAdapter(private val fragmentManager: FragmentManager, private va class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { - fun bind(action: Action, fragmentManager: FragmentManager, recyclerView: RecyclerView.Adapter, position : Int, actionList: MutableList) { + fun bind(action: Action, fragmentManager: FragmentManager, recyclerView: RecyclerView.Adapter, position: Int, actionList: MutableList) { view.findViewById(R.id.automation_layoutText).setOnClickListener { if (action.hasDialog()) { val args = Bundle() @@ -48,6 +48,7 @@ class ActionListAdapter(private val fragmentManager: FragmentManager, private va recyclerView.notifyDataSetChanged() RxBus.send(EventAutomationUpdateGui()) } + if (action.icon().isPresent) view.findViewById(R.id.automation_action_image).setImageResource(action.icon().get()) view.findViewById(R.id.automation_viewActionTitle).text = action.shortDescription() } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseActionDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseActionDialog.kt index 2096d5a541..c6097a1e88 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseActionDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseActionDialog.kt @@ -5,19 +5,18 @@ 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.dialogs.DialogFragmentWithDate 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() { +class ChooseActionDialog : DialogFragmentWithDate() { - var checkedIndex = -1 + private var checkedIndex = -1 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -26,7 +25,7 @@ class ChooseActionDialog : DialogFragment() { checkedIndex = bundle.getInt("checkedIndex") } - dialog?.setCanceledOnTouchOutside(false) + onCreateViewGeneral() return inflater.inflate(R.layout.automation_dialog_choose_action, container, false) } @@ -42,27 +41,19 @@ class ChooseActionDialog : DialogFragment() { 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()) - } + override fun submit(): Boolean { + instantiateAction()?.let { + RxBus.send(EventAutomationAddAction(it)) + RxBus.send(EventAutomationUpdateGui()) } - - // Cancel button - cancel.setOnClickListener { dismiss() } + return true } - override fun onStart() { - super.onStart() - dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - } - - override fun onSaveInstanceState(bundle: Bundle) { - bundle.putInt("checkedIndex", determineCheckedIndex()) + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putInt("checkedIndex", determineCheckedIndex()) } private fun instantiateAction(): Action? { @@ -86,5 +77,4 @@ class ChooseActionDialog : DialogFragment() { } return -1 } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseTriggerDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseTriggerDialog.kt index b2317fc6a2..eef3a7057e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseTriggerDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseTriggerDialog.kt @@ -5,17 +5,15 @@ 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.dialogs.DialogFragmentWithDate 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() { +class ChooseTriggerDialog : DialogFragmentWithDate() { private var checkedIndex = -1 - private var clickListener: OnClickListener? = null interface OnClickListener { @@ -29,7 +27,7 @@ class ChooseTriggerDialog : DialogFragment() { checkedIndex = bundle.getInt("checkedIndex") } - dialog?.setCanceledOnTouchOutside(false) + onCreateViewGeneral() return inflater.inflate(R.layout.automation_dialog_choose_trigger, container, false) } @@ -45,30 +43,22 @@ class ChooseTriggerDialog : DialogFragment() { if (checkedIndex != -1) (automation_chooseTriggerRadioGroup.getChildAt(checkedIndex) as RadioButton).isChecked = true - - // OK button - ok.setOnClickListener { - dismiss() - instantiateTrigger()?.let { - clickListener?.onClick(it) - } - } - - // Cancel button - cancel.setOnClickListener { dismiss() } } - override fun onStart() { - super.onStart() - dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + override fun submit(): Boolean { + instantiateTrigger()?.let { + clickListener?.onClick(it) + } + return true } fun setOnClickListener(clickListener: OnClickListener) { this.clickListener = clickListener } - override fun onSaveInstanceState(bundle: Bundle) { - bundle.putInt("checkedIndex", determineCheckedIndex()) + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putInt("checkedIndex", determineCheckedIndex()) } private fun instantiateTrigger(): Trigger? { @@ -92,5 +82,4 @@ class ChooseTriggerDialog : DialogFragment() { } return -1 } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditActionDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditActionDialog.kt index 2c49f81bba..e738b7f94c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditActionDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditActionDialog.kt @@ -4,16 +4,15 @@ 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.dialogs.DialogFragmentWithDate 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() { +class EditActionDialog : DialogFragmentWithDate() { private var action: Action? = null private var actionPosition: Int = -1 @@ -24,8 +23,7 @@ class EditActionDialog : DialogFragment() { actionPosition = bundle.getInt("actionPosition", -1) bundle.getString("action")?.let { action = Action.instantiate(JSONObject(it)) } } - - dialog?.setCanceledOnTouchOutside(false) + onCreateViewGeneral() return inflater.inflate(R.layout.automation_dialog_action, container, false) } @@ -37,22 +35,13 @@ class EditActionDialog : DialogFragment() { 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 onStart() { - super.onStart() - dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + override fun submit(): Boolean { + action?.let { + RxBus.send(EventAutomationUpdateAction(it, actionPosition)) + } + return true } override fun onSaveInstanceState(bundle: Bundle) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt index 85aab0f538..0bcb183e11 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt @@ -4,9 +4,9 @@ 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.dialogs.DialogFragmentWithDate import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.automation.AutomationEvent import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin @@ -17,9 +17,8 @@ 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() { +class EditEventDialog : DialogFragmentWithDate() { private var actionListAdapter: ActionListAdapter? = null private var event: AutomationEvent = AutomationEvent() @@ -35,7 +34,7 @@ class EditEventDialog : DialogFragment() { bundle.getString("event")?.let { event = AutomationEvent().fromJSON(it) } } - dialog?.setCanceledOnTouchOutside(false) + onCreateViewGeneral() return inflater.inflate(R.layout.automation_dialog_event, container, false) } @@ -60,39 +59,6 @@ class EditEventDialog : DialogFragment() { 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 @@ -137,9 +103,33 @@ class EditEventDialog : DialogFragment() { ) } - override fun onStart() { - super.onStart() - dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + override fun submit() : Boolean{ + // check for title + val title = automation_inputEventTitle.text.toString() + if (title.isEmpty()) { + ToastUtils.showToastInUiThread(context, R.string.automation_missing_task_name) + return false + } + 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 false + } + // check for at least one action + if (event.actions.isEmpty()) { + ToastUtils.showToastInUiThread(context, R.string.automation_missing_action) + return false + } + // store + if (position == -1) + AutomationPlugin.automationEvents.add(event) + else + AutomationPlugin.automationEvents[position] = event + + RxBus.send(EventAutomationDataChanged()) + return true } override fun onDestroyView() { @@ -147,10 +137,10 @@ class EditEventDialog : DialogFragment() { disposable.clear() } - override fun onSaveInstanceState(bundle: Bundle) { - super.onSaveInstanceState(bundle) - bundle.putString("event", event.toJSON()) - bundle.putInt("position", position) + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + savedInstanceState.putString("event", event.toJSON()) + savedInstanceState.putInt("position", position) } private fun showPreconditions() { @@ -164,5 +154,4 @@ class EditEventDialog : DialogFragment() { automation_forcedTriggerDescriptionLabel.visibility = View.GONE } } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditTriggerDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditTriggerDialog.kt index eda9c6177f..b30a724ec5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditTriggerDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditTriggerDialog.kt @@ -4,15 +4,14 @@ 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.dialogs.DialogFragmentWithDate 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() { +class EditTriggerDialog : DialogFragmentWithDate() { private var trigger: Trigger? = null @@ -23,7 +22,7 @@ class EditTriggerDialog : DialogFragment() { bundle.getString("trigger")?.let { trigger = Trigger.instantiate(it) } } - dialog?.setCanceledOnTouchOutside(false) + onCreateViewGeneral() return inflater.inflate(R.layout.automation_dialog_edit_trigger, container, false) } @@ -32,24 +31,15 @@ class EditTriggerDialog : DialogFragment() { // display root trigger trigger?.generateDialog(automation_layoutTrigger, fragmentManager) - - // OK button - ok.setOnClickListener { - dismiss() - trigger?.let { trigger -> RxBus.send(EventAutomationUpdateTrigger(trigger)) } - } - - // Cancel button - cancel.setOnClickListener { dismiss() } } - override fun onStart() { - super.onStart() - dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + override fun submit():Boolean { + trigger?.let { trigger -> RxBus.send(EventAutomationUpdateTrigger(trigger)) } + return true } - override fun onSaveInstanceState(bundle: Bundle) { - super.onSaveInstanceState(bundle) - trigger?.let { bundle.putString("trigger", it.toJSON()) } + override fun onSaveInstanceState(savedInstanceState: Bundle) { + super.onSaveInstanceState(savedInstanceState) + trigger?.let { savedInstanceState.putString("trigger", it.toJSON()) } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/TriggerListAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/TriggerListAdapter.java index 98d225d84e..3daa0fbd17 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/TriggerListAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/TriggerListAdapter.java @@ -75,7 +75,7 @@ public class TriggerListAdapter { private Spinner createSpinner() { Spinner spinner = new Spinner(mContext); - ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(mContext, android.R.layout.simple_spinner_item, TriggerConnector.Type.labels()); + ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(mContext, R.layout.spinner_centered, TriggerConnector.Type.labels()); spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(spinnerArrayAdapter); return spinner; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperAdapter.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperAdapter.kt new file mode 100644 index 0000000000..bdd68080ef --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperAdapter.kt @@ -0,0 +1,33 @@ +package info.nightscout.androidaps.plugins.general.automation.dragHelpers + + +interface ItemTouchHelperAdapter { + /** + * Called when an item has been dragged far enough to trigger a move. This is called every time + * an item is shifted, and **not** at the end of a "drop" event.

+ *

+ * Implementations should call [RecyclerView.Adapter.notifyItemMoved] after + * adjusting the underlying data to reflect this move. + * + * @param fromPosition The start position of the moved item. + * @param toPosition Then resolved position of the moved item. + * @return True if the item was moved to the new adapter position. + * + * @see RecyclerView.getAdapterPositionFor + * @see RecyclerView.ViewHolder.getAdapterPosition + */ + fun onItemMove(fromPosition: Int, toPosition: Int): Boolean + + /** + * Called when an item has been dismissed by a swipe.

+ *

+ * Implementations should call [RecyclerView.Adapter.notifyItemRemoved] after + * adjusting the underlying data to reflect this removal. + * + * @param position The position of the item dismissed. + * + * @see RecyclerView.getAdapterPositionFor + * @see RecyclerView.ViewHolder.getAdapterPosition + */ + fun onItemDismiss(position: Int) +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperViewHolder.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperViewHolder.kt new file mode 100644 index 0000000000..a873e5253d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/ItemTouchHelperViewHolder.kt @@ -0,0 +1,20 @@ +package info.nightscout.androidaps.plugins.general.automation.dragHelpers + +/** + * Interface to notify an item ViewHolder of relevant callbacks from [ ]. + * + * @author Paul Burke (ipaulpro) + */ +interface ItemTouchHelperViewHolder { + /** + * Called when the [ItemTouchHelper] first registers an item as being moved or swiped. + * Implementations should update the item view to indicate it's active state. + */ + fun onItemSelected() + + /** + * Called when the [ItemTouchHelper] has completed the move or swipe, and the active item + * state should be cleared. + */ + fun onItemClear() +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/OnStartDragListener.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/OnStartDragListener.kt new file mode 100644 index 0000000000..9a90a360de --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/OnStartDragListener.kt @@ -0,0 +1,12 @@ +package info.nightscout.androidaps.plugins.general.automation.dragHelpers + +import androidx.recyclerview.widget.RecyclerView + +interface OnStartDragListener { + /** + * Called when a view is requesting a start of a drag. + * + * @param viewHolder The holder of the view to drag. + */ + fun onStartDrag(viewHolder: RecyclerView.ViewHolder) +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/SimpleItemTouchHelperCallback.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/SimpleItemTouchHelperCallback.kt new file mode 100644 index 0000000000..7e66046e73 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dragHelpers/SimpleItemTouchHelperCallback.kt @@ -0,0 +1,85 @@ +package info.nightscout.androidaps.plugins.general.automation.dragHelpers + +import android.graphics.Canvas +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import kotlin.math.abs + + +/** + * An implementation of [ItemTouchHelper.Callback] that enables basic drag & drop and + * swipe-to-dismiss. Drag events are automatically started by an item long-press.

+ * + * Expects the `RecyclerView.Adapter` to listen for [ ] callbacks and the `RecyclerView.ViewHolder` to implement + * [ItemTouchHelperViewHolder]. + * + */ +class SimpleItemTouchHelperCallback(private val mAdapter: ItemTouchHelperAdapter) : ItemTouchHelper.Callback() { + override fun isLongPressDragEnabled(): Boolean { + return true + } + + override fun isItemViewSwipeEnabled(): Boolean { + return true + } + + override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { // Set movement flags based on the layout manager + return if (recyclerView.layoutManager is GridLayoutManager) { + val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT + val swipeFlags = 0 + makeMovementFlags(dragFlags, swipeFlags) + } else { + val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN + val swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END + makeMovementFlags(dragFlags, swipeFlags) + } + } + + override fun onMove(recyclerView: RecyclerView, source: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + if (source.itemViewType != target.itemViewType) { + return false + } + // Notify the adapter of the move + mAdapter.onItemMove(source.adapterPosition, target.adapterPosition) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, i: Int) { // Notify the adapter of the dismissal + mAdapter.onItemDismiss(viewHolder.adapterPosition) + } + + override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) { + if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { // Fade out the view as it is swiped out of the parent's bounds + val alpha = ALPHA_FULL - abs(dX) / viewHolder.itemView.width.toFloat() + viewHolder.itemView.alpha = alpha + viewHolder.itemView.translationX = dX + } else { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) + } + } + + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { // We only want the active item to change + if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { + if (viewHolder is ItemTouchHelperViewHolder) { // Let the view holder know that this item is being moved or dragged + val itemViewHolder: ItemTouchHelperViewHolder = viewHolder + itemViewHolder.onItemSelected() + } + } + super.onSelectedChanged(viewHolder, actionState) + } + + override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { + super.clearView(recyclerView, viewHolder) + viewHolder.itemView.alpha = ALPHA_FULL + if (viewHolder is ItemTouchHelperViewHolder) { // Tell the view holder it's time to restore the idle state + val itemViewHolder: ItemTouchHelperViewHolder = viewHolder + itemViewHolder.onItemClear() + } + } + + companion object { + const val ALPHA_FULL = 1.0f + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/Comparator.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/Comparator.java index 334adfcc01..c0e23d979c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/Comparator.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/Comparator.java @@ -87,7 +87,7 @@ public class Comparator extends Element { @Override public void addToLayout(LinearLayout root) { Spinner spinner = new Spinner(root.getContext()); - ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, Compare.labels()); + ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), R.layout.spinner_centered, Compare.labels()); spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(spinnerArrayAdapter); LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams( diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/ComparatorExists.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/ComparatorExists.java index 2bde0cb842..4bc0082afa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/ComparatorExists.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/ComparatorExists.java @@ -54,7 +54,7 @@ public class ComparatorExists extends Element { @Override public void addToLayout(LinearLayout root) { Spinner spinner = new Spinner(root.getContext()); - ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, Compare.labels()); + ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), R.layout.spinner_centered, Compare.labels()); spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(spinnerArrayAdapter); LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams( diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputBg.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputBg.java index 0809ed96d8..ff4781552a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputBg.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputBg.java @@ -25,7 +25,7 @@ public class InputBg extends Element { public InputBg() { super(); - setUnits(ProfileFunctions.getInstance().getProfileUnits()); + setUnits(ProfileFunctions.getSystemUnits()); if (getUnits().equals(Constants.MMOL)) value = MMOL_MIN; else diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDelta.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDelta.java index ad0e84bec4..4dc9358f6c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDelta.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDelta.java @@ -87,7 +87,7 @@ public class InputDelta extends Element { @Override public void addToLayout(LinearLayout root) { Spinner spinner = new Spinner(root.getContext()); - ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, DeltaType.labels()); + ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), R.layout.spinner_centered, DeltaType.labels()); spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(spinnerArrayAdapter); LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams( diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputLocationMode.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputLocationMode.java new file mode 100644 index 0000000000..3fea8b7c33 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputLocationMode.java @@ -0,0 +1,107 @@ +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 InputLocationMode extends Element { + + public enum Mode { + INSIDE, + OUTSIDE, + GOING_IN, + GOING_OUT; + + public @StringRes + int getStringRes() { + switch (this) { + case INSIDE: + return R.string.location_inside; + case OUTSIDE: + return R.string.location_outside; + case GOING_IN: + return R.string.location_going_in; + case GOING_OUT: + return R.string.location_going_out; + default: + return R.string.unknown; + } + } + + public static List labels() { + List list = new ArrayList<>(); + for (Mode c : Mode.values()) { + list.add(MainApp.gs(c.getStringRes())); + } + return list; + } + + public Mode fromString(String wanted){ + for (Mode c : Mode.values()) { + if(c.toString() == wanted) + return c; + } + return null; + } + } + + private Mode mode; + + public InputLocationMode() { + super(); + mode = Mode.INSIDE; + } + + public InputLocationMode(InputLocationMode another) { + super(); + this.mode = another.mode; + } + + @Override + public void addToLayout(LinearLayout root) { + ArrayAdapter adapter = new ArrayAdapter<>(root.getContext(), + R.layout.spinner_centered, Mode.labels()); + Spinner spinner = new Spinner(root.getContext()); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + 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) { + setValue(Mode.values()[position]); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + spinner.setSelection(this.getValue().ordinal()); + root.addView(spinner); + + } + + public Mode getValue() { + return mode; + } + + public InputLocationMode setValue(Mode mode) { + this.mode = mode; + return this; + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTempTarget.java index 7e9207c0e1..e0fabcf0dd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTempTarget.java @@ -37,7 +37,7 @@ public class InputTempTarget extends Element { public InputTempTarget() { super(); - setUnits(ProfileFunctions.getInstance().getProfileUnits()); + setUnits(ProfileFunctions.getSystemUnits()); if (getUnits().equals(Constants.MMOL)) value = 6; else diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java index f6ba6b6f67..c1bca15028 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java @@ -1,22 +1,25 @@ 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.appcompat.app.AppCompatActivity; 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 javax.annotation.Nullable; public abstract class Trigger { + private static final Logger log = LoggerFactory.getLogger(Trigger.class); TriggerConnector connector = null; long lastRun; @@ -56,7 +59,7 @@ public abstract class Trigger { try { return instantiate(new JSONObject(json)); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return null; } @@ -69,7 +72,7 @@ public abstract class Trigger { Class clazz = Class.forName(type); return ((Trigger) clazz.newInstance()).fromJSON(data.toString()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return null; } @@ -81,11 +84,11 @@ public abstract class Trigger { } @Nullable - Activity scanForActivity(Context cont) { + AppCompatActivity scanForActivity(Context cont) { if (cont == null) return null; - else if (cont instanceof Activity) - return (Activity) cont; + else if (cont instanceof AppCompatActivity) + return (AppCompatActivity) cont; else if (cont instanceof ContextWrapper) return scanForActivity(((ContextWrapper) cont).getBaseContext()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java index da850b187d..ce0a8f63bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAutosensValue.java @@ -89,7 +89,7 @@ public class TriggerAutosensValue extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -102,7 +102,7 @@ public class TriggerAutosensValue extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java index 1636377bf2..3733859b96 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java @@ -104,7 +104,7 @@ public class TriggerBg extends Trigger { data.put("units", bg.getUnits()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -118,7 +118,7 @@ public class TriggerBg extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java index 0ad9f75ab2..0a038112e2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java @@ -86,7 +86,7 @@ public class TriggerBolusAgo extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -99,7 +99,7 @@ public class TriggerBolusAgo extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java index 4ddeb4299a..bf17dc72d1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerCOB.java @@ -87,7 +87,7 @@ public class TriggerCOB extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -100,7 +100,7 @@ public class TriggerCOB extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java index ae4bd35ad5..549925528a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java @@ -162,7 +162,7 @@ public class TriggerConnector extends Trigger { data.put("triggerList", array); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -179,7 +179,7 @@ public class TriggerConnector extends Trigger { add(newItem); } } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java index a3939a60b7..6fb2053fbc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java @@ -45,7 +45,7 @@ public class TriggerDelta extends Trigger { public TriggerDelta() { super(); - this.units = ProfileFunctions.getInstance().getProfileUnits(); + this.units = ProfileFunctions.getSystemUnits(); initializer(); } @@ -129,7 +129,7 @@ public class TriggerDelta extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -144,7 +144,7 @@ public class TriggerDelta extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java index c4003af861..a3935d7cec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerIob.java @@ -82,7 +82,7 @@ public class TriggerIob extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -95,7 +95,7 @@ public class TriggerIob extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java index 4772d29112..8070e074be 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java @@ -19,6 +19,7 @@ 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.InputLocationMode; 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; @@ -28,12 +29,17 @@ import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.JsonHelper; import info.nightscout.androidaps.utils.T; +import static info.nightscout.androidaps.plugins.general.automation.elements.InputLocationMode.Mode.*; + 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")); + InputLocationMode modeSelected = new InputLocationMode(); + InputLocationMode.Mode lastMode = INSIDE; + InputString name = new InputString(); private Runnable buttonAction = () -> { @@ -54,13 +60,16 @@ public class TriggerLocation extends Trigger { latitude = new InputDouble(triggerLocation.latitude); longitude = new InputDouble(triggerLocation.longitude); distance = new InputDouble(triggerLocation.distance); + modeSelected = new InputLocationMode(triggerLocation.modeSelected); + if (modeSelected.getValue() == GOING_OUT) + lastMode = OUTSIDE; lastRun = triggerLocation.lastRun; name = triggerLocation.name; } @Override public synchronized boolean shouldRun() { - Location location = LocationService.getLastLocation(); + Location location = this.getCurrentLocation(); if (location == null) return false; @@ -72,11 +81,20 @@ public class TriggerLocation extends Trigger { a.setLongitude(longitude.getValue()); double calculatedDistance = location.distanceTo(a); - if (calculatedDistance < distance.getValue()) { +// log.debug("Moded(current/last/wanted): "+(currentMode(calculatedDistance))+"/"+lastMode+"/"+modeSelected.getValue()); +// log.debug("Distance: "+calculatedDistance + "("+distance.getValue()+")"); + + if ((modeSelected.getValue() == INSIDE) && (calculatedDistance <= distance.getValue()) || + ((modeSelected.getValue() == OUTSIDE) && (calculatedDistance > distance.getValue())) || + ((modeSelected.getValue() == GOING_IN) && (calculatedDistance <= distance.getValue()) && (lastMode == OUTSIDE)) || + ((modeSelected.getValue() == GOING_OUT) && (calculatedDistance > distance.getValue()) && (lastMode == INSIDE)) + ) { if (L.isEnabled(L.AUTOMATION)) log.debug("Ready for execution: " + friendlyDescription()); + lastMode = currentMode(calculatedDistance); return true; } + lastMode = currentMode(calculatedDistance); // current mode will be last mode for the next check return false; } @@ -90,10 +108,11 @@ public class TriggerLocation extends Trigger { data.put("longitude", longitude.getValue()); data.put("distance", distance.getValue()); data.put("name", name.getValue()); + data.put("mode", modeSelected.getValue()); data.put("lastRun", lastRun); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -106,9 +125,12 @@ public class TriggerLocation extends Trigger { longitude.setValue(JsonHelper.safeGetDouble(d, "longitude")); distance.setValue(JsonHelper.safeGetDouble(d, "distance")); name.setValue(JsonHelper.safeGetString(d, "name")); - lastRun = JsonHelper.safeGetLong(d, "lastRun"); + modeSelected.setValue(InputLocationMode.Mode.valueOf(JsonHelper.safeGetString(d, "mode"))); + if (modeSelected.getValue() == GOING_OUT) + lastMode = OUTSIDE; + lastRun = DateUtil.now(); // set lastRun to now to give the service 5 mins to get the location properly } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } @@ -120,7 +142,7 @@ public class TriggerLocation extends Trigger { @Override public String friendlyDescription() { - return MainApp.gs(R.string.locationis, name.getValue()); + return MainApp.gs(R.string.locationis, MainApp.gs(modeSelected.getValue().getStringRes()), " " + name.getValue()); } @Override @@ -154,6 +176,11 @@ public class TriggerLocation extends Trigger { return this; } + TriggerLocation setMode(InputLocationMode.Mode value) { + modeSelected.setValue(value); + return this; + } + @Override public void generateDialog(LinearLayout root, FragmentManager fragmentManager) { new LayoutBuilder() @@ -162,7 +189,21 @@ public class TriggerLocation extends Trigger { .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 LabelWithElement(MainApp.gs(R.string.location_mode), "", modeSelected)) .add(new InputButton(MainApp.gs(R.string.currentlocation), buttonAction), LocationService.getLastLocation() != null) .build(root); } + + // Method to return the actual mode based on the current distance + InputLocationMode.Mode currentMode(double currentDistance){ + if ( currentDistance <= this.distance.getValue() ) + return INSIDE; + else + return OUTSIDE; + } + + static Location getCurrentLocation(){ + return LocationService.getLastLocation(); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java index d86bc35cb8..e4c0ae4346 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerProfilePercent.java @@ -88,7 +88,7 @@ public class TriggerProfilePercent extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -101,7 +101,7 @@ public class TriggerProfilePercent extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java index 8cec3bdf44..ff8bb335f9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerPumpLastConnection.java @@ -82,7 +82,7 @@ public class TriggerPumpLastConnection extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -95,7 +95,7 @@ public class TriggerPumpLastConnection extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java index a145253b36..2031dbc74c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerRecurringTime.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.general.automation.triggers; -import android.app.Activity; import android.graphics.Typeface; import android.text.format.DateFormat; import android.view.ViewGroup; @@ -9,6 +8,7 @@ import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.StringRes; +import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; import com.dpro.widgets.WeekdaysPicker; @@ -163,7 +163,7 @@ public class TriggerRecurringTime extends Trigger { object.put("type", TriggerRecurringTime.class.getName()); object.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return object.toString(); } @@ -181,7 +181,7 @@ public class TriggerRecurringTime extends Trigger { minute = JsonHelper.safeGetInt(o, "minute"); validTo = JsonHelper.safeGetLong(o, "validTo"); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } @@ -269,6 +269,8 @@ public class TriggerRecurringTime extends Trigger { weekdaysPicker.setSelectedDays(getSelectedDays()); weekdaysPicker.setOnWeekdaysChangeListener((view, i, list) -> set(DayOfWeek.fromCalendarInt(i), list.contains(i))); weekdaysPicker.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + weekdaysPicker.setSundayFirstDay(Calendar.getInstance().getFirstDayOfWeek() == Calendar.SUNDAY); + weekdaysPicker.redrawDays(); root.addView(weekdaysPicker); @@ -294,9 +296,9 @@ public class TriggerRecurringTime extends Trigger { ); tpd.setThemeDark(true); tpd.dismissOnPause(true); - Activity a = scanForActivity(root.getContext()); + AppCompatActivity a = scanForActivity(root.getContext()); if (a != null) - tpd.show(a.getFragmentManager(), "TimePickerDialog"); + tpd.show(a.getSupportFragmentManager(), "TimePickerDialog"); }); int px = MainApp.dpToPx(10); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java index 378652ed2a..a5fc6ba092 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTempTarget.java @@ -74,7 +74,7 @@ public class TriggerTempTarget extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -86,14 +86,14 @@ public class TriggerTempTarget extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(ComparatorExists.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } @Override public int friendlyName() { - return R.string.temptarget; + return R.string.careportal_temporarytarget; } @Override @@ -124,7 +124,7 @@ public class TriggerTempTarget extends Trigger { @Override public void generateDialog(LinearLayout root, FragmentManager fragmentManager) { new LayoutBuilder() - .add(new StaticLabel(R.string.temptarget)) + .add(new StaticLabel(R.string.careportal_temporarytarget)) .add(comparator) .build(root); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java index 261783b99c..91e739ca45 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java @@ -1,12 +1,12 @@ package info.nightscout.androidaps.plugins.general.automation.triggers; -import android.app.Activity; import android.graphics.Typeface; import android.text.format.DateFormat; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; import com.google.common.base.Optional; @@ -65,7 +65,7 @@ public class TriggerTime extends Trigger { object.put("type", TriggerTime.class.getName()); object.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return object.toString(); } @@ -78,7 +78,7 @@ public class TriggerTime extends Trigger { lastRun = JsonHelper.safeGetLong(o, "lastRun"); runAt = JsonHelper.safeGetLong(o, "runAt"); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } @@ -142,9 +142,9 @@ public class TriggerTime extends Trigger { ); dpd.setThemeDark(true); dpd.dismissOnPause(true); - Activity a = scanForActivity(root.getContext()); + AppCompatActivity a = scanForActivity(root.getContext()); if (a != null) - dpd.show(a.getFragmentManager(), "DatePickerDialog"); + dpd.show(a.getSupportFragmentManager(), "DatePickerDialog"); }); timeButton.setOnClickListener(view -> { GregorianCalendar calendar = new GregorianCalendar(); @@ -162,9 +162,9 @@ public class TriggerTime extends Trigger { ); tpd.setThemeDark(true); tpd.dismissOnPause(true); - Activity a = scanForActivity(root.getContext()); + AppCompatActivity a = scanForActivity(root.getContext()); if (a != null) - tpd.show(a.getFragmentManager(), "TimePickerDialog"); + tpd.show(a.getSupportFragmentManager(), "TimePickerDialog"); }); int px = MainApp.dpToPx(10); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java index 8755c3559c..adcd85bc71 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTimeRange.java @@ -1,12 +1,12 @@ package info.nightscout.androidaps.plugins.general.automation.triggers; -import android.app.Activity; import android.graphics.Typeface; import android.text.format.DateFormat; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; import com.google.common.base.Optional; @@ -18,7 +18,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Calendar; -import java.util.Date; import java.util.GregorianCalendar; import info.nightscout.androidaps.MainApp; @@ -33,14 +32,14 @@ import info.nightscout.androidaps.utils.T; public class TriggerTimeRange extends Trigger { private static Logger log = LoggerFactory.getLogger(L.AUTOMATION); - + // in minutes since midnight 60 means 1AM private int start; private int end; long timeZoneOffset = DateUtil.getTimeZoneOffsetMs(); public TriggerTimeRange() { - + start = getMinSinceMidnight(DateUtil.now()); end = getMinSinceMidnight(DateUtil.now()); } @@ -60,11 +59,11 @@ public class TriggerTimeRange extends Trigger { return false; boolean doRun = false; - if ( start < end && start < currentMinSinceMidnight && currentMinSinceMidnight < end) + if (start < end && start < currentMinSinceMidnight && currentMinSinceMidnight < end) doRun = true; - // handle cases like 10PM to 6AM - else if ( start > end && (start < currentMinSinceMidnight || currentMinSinceMidnight < end)) + // handle cases like 10PM to 6AM + else if (start > end && (start < currentMinSinceMidnight || currentMinSinceMidnight < end)) doRun = true; if (doRun) { @@ -93,7 +92,7 @@ public class TriggerTimeRange extends Trigger { object.put("type", TriggerTimeRange.class.getName()); object.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } log.debug(object.toString()); return object.toString(); @@ -108,7 +107,7 @@ public class TriggerTimeRange extends Trigger { start = JsonHelper.safeGetInt(o, "start"); end = JsonHelper.safeGetInt(o, "end"); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } @@ -129,8 +128,8 @@ public class TriggerTimeRange extends Trigger { } TriggerTimeRange period(int start, int end) { - this.start = getMinSinceMidnight(start*60000); - this.end = getMinSinceMidnight(end*60000); + this.start = getMinSinceMidnight(start * 60000); + this.end = getMinSinceMidnight(end * 60000); return this; } @@ -145,23 +144,23 @@ public class TriggerTimeRange extends Trigger { } long toMilis(long minutesSinceMidnight) { - return minutesSinceMidnight*60*1000; + return minutesSinceMidnight * 60 * 1000; } public int getMinSinceMidnight(long time) { // if passed argument is smaller than 1440 ( 24 h * 60 min ) that value is already converted - if (0 < time && time < 1441) + if (0 <= time && time < 1441) return (int) time; Calendar calendar = DateUtil.gregorianCalendar(); calendar.setTimeInMillis(time); return (calendar.get(Calendar.HOUR_OF_DAY) * 60) + calendar.get(Calendar.MINUTE); } - int getStart(){ + int getStart() { return start; } - int getEnd(){ + int getEnd() { return end; } @@ -170,8 +169,8 @@ public class TriggerTimeRange extends Trigger { TextView label = new TextView(root.getContext()); TextView startButton = new TextView(root.getContext()); TextView endButton = new TextView(root.getContext()); - log.debug("Start is: " + start ); - log.debug("End is: " + end ); + log.debug("Start is: " + start); + log.debug("End is: " + end); startButton.setText(DateUtil.timeString(toMilis(start) - timeZoneOffset)); endButton.setText(MainApp.gs(R.string.and) + " " + DateUtil.timeString(toMilis(end) - timeZoneOffset)); @@ -193,9 +192,9 @@ public class TriggerTimeRange extends Trigger { ); tpd.setThemeDark(true); tpd.dismissOnPause(true); - Activity a = scanForActivity(root.getContext()); + AppCompatActivity a = scanForActivity(root.getContext()); if (a != null) - tpd.show(a.getFragmentManager(), "TimePickerDialog"); + tpd.show(a.getSupportFragmentManager(), "TimePickerDialog"); }); endButton.setOnClickListener(view -> { GregorianCalendar calendar = new GregorianCalendar(); @@ -213,9 +212,9 @@ public class TriggerTimeRange extends Trigger { ); tpd.setThemeDark(true); tpd.dismissOnPause(true); - Activity a = scanForActivity(root.getContext()); + AppCompatActivity a = scanForActivity(root.getContext()); if (a != null) - tpd.show(a.getFragmentManager(), "TimePickerDialog"); + tpd.show(a.getSupportFragmentManager(), "TimePickerDialog"); }); int px = MainApp.dpToPx(10); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java index ba10e81583..dd2c34bcb8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerWifiSsid.java @@ -85,7 +85,7 @@ public class TriggerWifiSsid extends Trigger { data.put("comparator", comparator.getValue().toString()); o.put("data", data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return o.toString(); } @@ -98,7 +98,7 @@ public class TriggerWifiSsid extends Trigger { lastRun = JsonHelper.safeGetLong(d, "lastRun"); comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalFragment.java index 42ae7fd6fc..5dd9292343 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalFragment.java @@ -63,7 +63,6 @@ public class CareportalFragment extends Fragment implements View.OnClickListener public static final OptionsToShow TEMPBASALSTART = new OptionsToShow(R.id.careportal_tempbasalstart, R.string.careportal_tempbasalstart).date().bg().duration().percent().absolute(); public static final OptionsToShow TEMPBASALEND = new OptionsToShow(R.id.careportal_tempbasalend, R.string.careportal_tempbasalend).date().bg(); public static final OptionsToShow PROFILESWITCH = new OptionsToShow(R.id.careportal_profileswitch, R.string.careportal_profileswitch).date().duration().profile(); - public static final OptionsToShow PROFILESWITCHDIRECT = new OptionsToShow(R.id.careportal_profileswitch, R.string.careportal_profileswitch).duration().profile(); public static final OptionsToShow OPENAPSOFFLINE = new OptionsToShow(R.id.careportal_openapsoffline, R.string.careportal_openapsoffline).date().duration(); public static final OptionsToShow TEMPTARGET = new OptionsToShow(R.id.careportal_temporarytarget, R.string.careportal_temporarytarget).date().duration().tempTarget(); @@ -180,7 +179,6 @@ public class CareportalFragment extends Fragment implements View.OnClickListener newDialog.setOptions(NOTE, R.string.careportal_note); break; case R.id.careportal_profileswitch: - PROFILESWITCH.executeProfileSwitch = false; newDialog.setOptions(PROFILESWITCH, R.string.careportal_profileswitch); break; case R.id.careportal_pumpsitechange: @@ -202,7 +200,6 @@ public class CareportalFragment extends Fragment implements View.OnClickListener newDialog.setOptions(OPENAPSOFFLINE, R.string.careportal_openapsoffline); break; case R.id.careportal_temporarytarget: - TEMPTARGET.executeTempTarget = false; newDialog.setOptions(TEMPTARGET, R.string.careportal_temporarytarget); break; default: @@ -222,7 +219,7 @@ public class CareportalFragment extends Fragment implements View.OnClickListener activity.runOnUiThread( () -> { CareportalEvent careportalEvent; - NSSettingsStatus nsSettings = new NSSettingsStatus().getInstance(); + NSSettingsStatus nsSettings = NSSettingsStatus.getInstance(); double iageUrgent = nsSettings.getExtendedWarnValue("iage", "urgent", 96); double iageWarn = nsSettings.getExtendedWarnValue("iage", "warn", 72); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalPlugin.java index fa8f87ef46..fc28d23188 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalPlugin.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.general.careportal; +import info.nightscout.androidaps.Config; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; @@ -22,10 +23,14 @@ public class CareportalPlugin extends PluginBase { .fragmentClass(CareportalFragment.class.getName()) .pluginName(R.string.careportal) .shortName(R.string.careportal_shortname) - .visibleByDefault(true) - .enableByDefault(true) + .visibleByDefault(Config.NSCLIENT) + .enableByDefault(Config.NSCLIENT) .description(R.string.description_careportal) ); } + @Override + public boolean specialEnableCondition() { + return Config.NSCLIENT; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/Dialogs/NewNSTreatmentDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/Dialogs/NewNSTreatmentDialog.java index 8818638201..38ad5bb879 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/Dialogs/NewNSTreatmentDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/Dialogs/NewNSTreatmentDialog.java @@ -1,16 +1,15 @@ package info.nightscout.androidaps.plugins.general.careportal.Dialogs; -import android.app.Activity; import android.os.Bundle; -import androidx.fragment.app.DialogFragment; -import androidx.appcompat.app.AlertDialog; import android.text.Editable; import android.text.TextWatcher; import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; @@ -20,9 +19,11 @@ import android.widget.RadioButton; import android.widget.Spinner; import android.widget.TextView; +import androidx.appcompat.app.AppCompatDialogFragment; +import androidx.fragment.app.DialogFragment; + import com.google.common.collect.Lists; import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; -import com.wdullaer.materialdatetimepicker.time.RadialPickerLayout; import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; import org.json.JSONException; @@ -39,39 +40,35 @@ import java.util.List; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.ProfileSwitch; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.careportal.OptionsToShow; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DefaultValueHelper; import info.nightscout.androidaps.utils.HardLimits; import info.nightscout.androidaps.utils.JsonHelper; import info.nightscout.androidaps.utils.NumberPicker; +import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.SafeParse; import info.nightscout.androidaps.utils.Translator; -public class NewNSTreatmentDialog extends DialogFragment implements View.OnClickListener, DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener { +public class NewNSTreatmentDialog extends AppCompatDialogFragment implements View.OnClickListener, DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener { private static Logger log = LoggerFactory.getLogger(NewNSTreatmentDialog.class); - private Activity context; - private static OptionsToShow options; private static String event; - Profile profile; + private Profile profile; public ProfileStore profileStore; - String units = Constants.MGDL; TextView eventTypeText; LinearLayout layoutPercent; @@ -107,9 +104,10 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick private static Integer seconds = null; - public void setOptions(OptionsToShow options, int event) { + public NewNSTreatmentDialog setOptions(OptionsToShow options, int event) { this.options = options; this.event = MainApp.gs(event); + return this; } public NewNSTreatmentDialog() { @@ -120,23 +118,14 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick } } - @Override - public void onAttach(Activity activity) { - context = activity; - super.onAttach(activity); - } - - @Override - public void onDetach() { - super.onDetach(); - this.context = null; - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (options == null) return null; - getDialog().setTitle(MainApp.gs(options.eventName)); + getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); + getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + setCancelable(true); + getDialog().setCanceledOnTouchOutside(false); setStyle(DialogFragment.STYLE_NORMAL, getTheme()); View view = inflater.inflate(R.layout.careportal_newnstreatment_dialog, container, false); @@ -179,7 +168,6 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick } } else { ArrayList profileList; - units = profile != null ? profile.getUnits() : Constants.MGDL; profileList = profileStore.getProfileList(); ArrayAdapter adapter = new ArrayAdapter<>(getContext(), R.layout.spinner_centered, profileList); @@ -190,7 +178,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick profileSpinner.setSelection(p); } } - final Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, units); + final Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, ProfileFunctions.getSystemUnits()); // temp target final List reasonList = Lists.newArrayList( @@ -205,8 +193,8 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { double defaultDuration; - double defaultTarget = 0; - if (profile != null && editTemptarget.getValue() == bg) { + double defaultTarget; + if (profile != null && editTemptarget.getValue().equals(bg)) { defaultTarget = bg; } else { //prevent changes on screen rotate @@ -214,17 +202,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick } boolean erase = false; - String units = ProfileFunctions.getInstance().getProfileUnits(); - DefaultValueHelper helper = new DefaultValueHelper(); + String units = ProfileFunctions.getSystemUnits(); if (MainApp.gs(R.string.eatingsoon).equals(reasonList.get(position))) { - defaultDuration = helper.determineEatingSoonTTDuration(); - defaultTarget = helper.determineEatingSoonTT(units); + defaultDuration = DefaultValueHelper.determineEatingSoonTTDuration(); + defaultTarget = DefaultValueHelper.determineEatingSoonTT(); } else if (MainApp.gs(R.string.activity).equals(reasonList.get(position))) { - defaultDuration = helper.determineActivityTTDuration(); - defaultTarget = helper.determineActivityTT(units); + defaultDuration = DefaultValueHelper.determineActivityTTDuration(); + defaultTarget = DefaultValueHelper.determineActivityTT(); } else if (MainApp.gs(R.string.hypo).equals(reasonList.get(position))) { - defaultDuration = helper.determineHypoTTDuration(); - defaultTarget = helper.determineHypoTT(units); + defaultDuration = DefaultValueHelper.determineHypoTTDuration(); + defaultTarget = DefaultValueHelper.determineHypoTT(); } else if (editDuration.getValue() != 0) { defaultDuration = editDuration.getValue(); } else { @@ -249,7 +236,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick }); // bg - bgUnitsView.setText(units); + bgUnitsView.setText(ProfileFunctions.getSystemUnits()); TextWatcher bgTextWatcher = new TextWatcher() { @@ -268,7 +255,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick if (profile == null) { editBg.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), bgTextWatcher); editTemptarget.setParams(Constants.MIN_TT_MGDL, Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok)); - } else if (units.equals(Constants.MMOL)) { + } else if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) { editBg.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok), bgTextWatcher); editTemptarget.setParams(Constants.MIN_TT_MMOL, Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok)); } else { @@ -277,7 +264,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick } sensorRadioButton.setOnCheckedChangeListener((buttonView, isChecked) -> { - Double bg1 = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, units); + Double bg1 = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, ProfileFunctions.getSystemUnits()); if (savedInstanceState != null && savedInstanceState.getDouble("editBg") != bg1) { editBg.setValue(savedInstanceState.getDouble("editBg")); } else { @@ -286,16 +273,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick }); Integer maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value(); - editCarbs = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_carbsinput); + editCarbs = view.findViewById(R.id.careportal_newnstreatment_carbsinput); editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok)); Double maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value(); - editInsulin = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_insulininput); + editInsulin = view.findViewById(R.id.careportal_newnstreatment_insulininput); editInsulin.setParams(0d, 0d, maxInsulin, 0.05d, new DecimalFormat("0.00"), false, view.findViewById(R.id.ok)); - editSplit = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_splitinput); + editSplit = view.findViewById(R.id.careportal_newnstreatment_splitinput); editSplit.setParams(100d, 0d, 100d, 5d, new DecimalFormat("0"), true, view.findViewById(R.id.ok)); - editDuration = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_durationinput); + editDuration = view.findViewById(R.id.careportal_newnstreatment_durationinput); editDuration.setParams(0d, 0d, Constants.MAX_PROFILE_SWITCH_DURATION, 10d, new DecimalFormat("0"), false, view.findViewById(R.id.ok)); TextWatcher percentTextWatcher = new TextWatcher() { @@ -319,7 +306,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick Integer maxPercent = 200; if (profile != null) maxPercent = MainApp.getConstraintChecker().getMaxBasalPercentAllowed(profile).value(); - editPercent = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_percentinput); + editPercent = view.findViewById(R.id.careportal_newnstreatment_percentinput); editPercent.setParams(0d, -100d, (double) maxPercent, 5d, new DecimalFormat("0"), true, view.findViewById(R.id.ok), percentTextWatcher); TextWatcher absoluteTextWatcher = new TextWatcher() { @@ -343,16 +330,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick Double maxAbsolute = HardLimits.maxBasal(); if (profile != null) maxAbsolute = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value(); - editAbsolute = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_absoluteinput); + editAbsolute = view.findViewById(R.id.careportal_newnstreatment_absoluteinput); editAbsolute.setParams(0d, 0d, maxAbsolute, 0.05d, new DecimalFormat("0.00"), true, view.findViewById(R.id.ok), absoluteTextWatcher); - editCarbTime = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_carbtimeinput); + editCarbTime = view.findViewById(R.id.careportal_newnstreatment_carbtimeinput); editCarbTime.setParams(0d, -60d, 60d, 5d, new DecimalFormat("0"), false, view.findViewById(R.id.ok)); - editPercentage = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_percentage); + editPercentage = view.findViewById(R.id.careportal_newnstreatment_percentage); editPercentage.setParams(100d, (double) Constants.CPP_MIN_PERCENTAGE, (double) Constants.CPP_MAX_PERCENTAGE, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok)); - editTimeshift = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_timeshift); + editTimeshift = view.findViewById(R.id.careportal_newnstreatment_timeshift); editTimeshift.setParams(0d, (double) Constants.CPP_MIN_TIMESHIFT, (double) Constants.CPP_MAX_TIMESHIFT, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok)); ProfileSwitch ps = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now()); @@ -369,21 +356,21 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick options.duration = false; } - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_eventtime_layout), options.date); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_bg_layout), options.bg); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_bgsource_layout), options.bg); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_insulin_layout), options.insulin); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_carbs_layout), options.carbs); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_split_layout), options.split); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_duration_layout), options.duration); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_eventtime_layout), options.date); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_bg_layout), options.bg); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_bgsource_layout), options.bg); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_insulin_layout), options.insulin); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_carbs_layout), options.carbs); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_split_layout), options.split); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_duration_layout), options.duration); showOrHide(layoutPercent, options.percent); showOrHide(layoutAbsolute, options.absolute); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_carbtime_layout), options.prebolus); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_profile_layout), options.profile); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_percentage_layout), options.profile); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_timeshift_layout), options.profile); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_reuse_layout), options.profile && ps != null && ps.isCPP); - showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_temptarget_layout), options.tempTarget); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_carbtime_layout), options.prebolus); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_profile_layout), options.profile); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_percentage_layout), options.profile); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_timeshift_layout), options.profile); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_reuse_layout), options.profile && ps != null && ps.isCPP); + showOrHide(view.findViewById(R.id.careportal_newnstreatment_temptarget_layout), options.tempTarget); setCancelable(true); getDialog().setCanceledOnTouchOutside(false); @@ -409,6 +396,13 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick return view; } + @Override + public void onResume() { + super.onResume(); + getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + } + @Override public void onClick(View view) { Calendar calendar = Calendar.getInstance(); @@ -423,18 +417,18 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick ); dpd.setThemeDark(true); dpd.dismissOnPause(true); - dpd.show(context.getFragmentManager(), "Datepickerdialog"); + dpd.show(getActivity().getSupportFragmentManager(), "Datepickerdialog"); break; case R.id.careportal_newnstreatment_eventtime: TimePickerDialog tpd = TimePickerDialog.newInstance( this, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), - DateFormat.is24HourFormat(context) + DateFormat.is24HourFormat(getContext()) ); tpd.setThemeDark(true); tpd.dismissOnPause(true); - tpd.show(context.getFragmentManager(), "Timepickerdialog"); + tpd.show(getActivity().getSupportFragmentManager(), "Timepickerdialog"); break; case R.id.ok: confirmNSTreatmentCreation(); @@ -457,7 +451,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick if ((data.size() > 0) && (data.get(0).date > millis - 7 * 60 * 1000L) && (data.get(0).date < millis + 7 * 60 * 1000L)) { - editBg.setValue(Profile.fromMgdlToUnits(data.get(0).value, units)); + editBg.setValue(Profile.fromMgdlToUnits(data.get(0).value, ProfileFunctions.getSystemUnits())); } } @@ -471,7 +465,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick } @Override - public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute, int second) { + public void onTimeSet(TimePickerDialog view, int hourOfDay, int minute, int second) { eventTime.setHours(hourOfDay); eventTime.setMinutes(minute); eventTime.setSeconds(this.seconds++); // randomize seconds to prevent creating record of the same time, if user choose time manually @@ -479,7 +473,6 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick updateBGforDateTime(); } - JSONObject gatherData() { String enteredBy = SP.getString("careportal_enteredby", ""); JSONObject data = new JSONObject(); @@ -585,7 +578,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick data.put("preBolus", SafeParse.stringToDouble(editCarbTime.getText())); if (!notesEdit.getText().toString().equals("")) data.put("notes", notesEdit.getText().toString()); - data.put("units", units); + data.put("units", ProfileFunctions.getSystemUnits()); if (!enteredBy.equals("")) data.put("enteredBy", enteredBy); if (options.eventType == R.id.careportal_combobolus) { Double enteredInsulin = SafeParse.stringToDouble(editInsulin.getText()); @@ -601,17 +594,17 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick String buildConfirmText(JSONObject data) { String ret = ""; - if (data.has("eventType")) { - ret += MainApp.gs(R.string.careportal_newnstreatment_eventtype); - ret += ": "; - ret += Translator.translate(JsonHelper.safeGetString(data, "eventType", "")); - ret += "\n"; - } +// if (data.has("eventType")) { +// ret += MainApp.gs(R.string.careportal_newnstreatment_eventtype); +// ret += ": "; +// ret += Translator.translate(JsonHelper.safeGetString(data, "eventType", "")); +// ret += "\n"; +// } if (data.has("glucose")) { ret += MainApp.gs(R.string.treatments_wizard_bg_label); ret += ": "; ret += JsonHelper.safeGetObject(data, "glucose", ""); - ret += " " + units + "\n"; + ret += " " + ProfileFunctions.getSystemUnits() + "\n"; } if (data.has("glucoseType")) { ret += MainApp.gs(R.string.careportal_newnstreatment_glucosetype); @@ -688,7 +681,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick ret += "\n"; } if (data.has("created_at")) { - ret += MainApp.gs(R.string.careportal_newnstreatment_eventtime_label); + ret += MainApp.gs(R.string.event_time_label); ret += ": "; ret += eventTime.toLocaleString(); ret += "\n"; @@ -703,60 +696,25 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick return ret; } - void confirmNSTreatmentCreation() { - if (context != null) { - final JSONObject data = gatherData(); - final String confirmText = buildConfirmText(data); - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(confirmText); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> createNSTreatment(data)); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - } + private void confirmNSTreatmentCreation() { + final JSONObject data = gatherData(); + OKDialog.showConfirmation(getContext(), Translator.translate(JsonHelper.safeGetString(data, "eventType", MainApp.gs(R.string.overview_treatment_label))), buildConfirmText(data), () -> createNSTreatment(data)); } - public void createNSTreatment(JSONObject data) { - if (options.executeProfileSwitch) { - if (data.has("profile")) { - ProfileFunctions.doProfileSwitch(profileStore, JsonHelper.safeGetString(data, "profile"), JsonHelper.safeGetInt(data, "duration"), JsonHelper.safeGetInt(data, "percentage"), JsonHelper.safeGetInt(data, "timeshift")); - } - } else if (options.executeTempTarget) { - final int duration = JsonHelper.safeGetInt(data, "duration"); - final double targetBottom = JsonHelper.safeGetDouble(data, "targetBottom"); - final double targetTop = JsonHelper.safeGetDouble(data, "targetTop"); - final String reason = JsonHelper.safeGetString(data, "reason", ""); - if ((targetBottom != 0d && targetTop != 0d) || duration == 0) { - TempTarget tempTarget = new TempTarget() - .date(eventTime.getTime()) - .duration(duration) - .reason(reason) - .source(Source.USER); - if (tempTarget.durationInMinutes != 0) { - tempTarget.low(Profile.toMgdl(targetBottom, units)) - .high(Profile.toMgdl(targetTop, units)); - } else { - tempTarget.low(0).high(0); - } - TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); - } - if (duration == 10) - SP.putBoolean(R.string.key_objectiveusetemptarget, true); + void createNSTreatment(JSONObject data) { + if (JsonHelper.safeGetString(data, "eventType", "").equals(CareportalEvent.PROFILESWITCH)) { + ProfileSwitch profileSwitch = ProfileFunctions.prepareProfileSwitch( + profileStore, + JsonHelper.safeGetString(data, "profile"), + JsonHelper.safeGetInt(data, "duration"), + JsonHelper.safeGetInt(data, "percentage"), + JsonHelper.safeGetInt(data, "timeshift"), + eventTime.getTime() + ); + NSUpload.uploadProfileSwitch(profileSwitch); } else { - if (JsonHelper.safeGetString(data, "eventType").equals(CareportalEvent.PROFILESWITCH)) { - ProfileSwitch profileSwitch = ProfileFunctions.prepareProfileSwitch( - profileStore, - JsonHelper.safeGetString(data, "profile"), - JsonHelper.safeGetInt(data, "duration"), - JsonHelper.safeGetInt(data, "percentage"), - JsonHelper.safeGetInt(data, "timeshift"), - eventTime.getTime() - ); - NSUpload.uploadProfileSwitch(profileSwitch); - } else { - NSUpload.uploadCareportalEntryToNS(data); - } + NSUpload.uploadCareportalEntryToNS(data); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/OptionsToShow.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/OptionsToShow.java index 08f65a967f..e1effcd561 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/OptionsToShow.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/OptionsToShow.java @@ -19,10 +19,6 @@ public class OptionsToShow { public boolean split; public boolean tempTarget; - // perform direct actions - public boolean executeProfileSwitch = false; - public boolean executeTempTarget = false; - public OptionsToShow(int eventType, int eventName) { this.eventType = eventType; this.eventName = eventName; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.java index 997c6b0ba9..074310a8dd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.general.food; -import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; import android.text.Editable; @@ -14,14 +13,11 @@ import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -33,6 +29,7 @@ import info.nightscout.androidaps.events.EventFoodDatabaseChanged; import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SpinnerHelper; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -42,43 +39,36 @@ import io.reactivex.disposables.CompositeDisposable; */ public class FoodFragment extends Fragment { - private static Logger log = LoggerFactory.getLogger(FoodFragment.class); private CompositeDisposable disposable = new CompositeDisposable(); - EditText filter; - ImageView clearFilter; - SpinnerHelper category; - SpinnerHelper subcategory; - RecyclerView recyclerView; + private EditText filter; + private SpinnerHelper category; + private SpinnerHelper subcategory; + private RecyclerView recyclerView; - List unfiltered; - List filtered; - ArrayList categories; - ArrayList subcategories; + private List unfiltered; + private List filtered; - final String EMPTY = MainApp.gs(R.string.none); + private final String EMPTY = MainApp.gs(R.string.none); @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.food_fragment, container, false); - filter = (EditText) view.findViewById(R.id.food_filter); - clearFilter = (ImageView) view.findViewById(R.id.food_clearfilter); + filter = view.findViewById(R.id.food_filter); + ImageView clearFilter = view.findViewById(R.id.food_clearfilter); category = new SpinnerHelper(view.findViewById(R.id.food_category)); subcategory = new SpinnerHelper(view.findViewById(R.id.food_subcategory)); - recyclerView = (RecyclerView) view.findViewById(R.id.food_recyclerview); + recyclerView = view.findViewById(R.id.food_recyclerview); recyclerView.setHasFixedSize(true); LinearLayoutManager llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); - clearFilter.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - filter.setText(""); - category.setSelection(0); - subcategory.setSelection(0); - filterData(); - } + clearFilter.setOnClickListener(v -> { + filter.setText(""); + category.setSelection(0); + subcategory.setSelection(0); + filterData(); }); category.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @@ -149,11 +139,11 @@ public class FoodFragment extends Fragment { disposable.clear(); } - void loadData() { + private void loadData() { unfiltered = FoodPlugin.getPlugin().getService().getFoodData(); } - void fillCategories() { + private void fillCategories() { Set catSet = new HashSet<>(); for (Food f : unfiltered) { @@ -162,7 +152,7 @@ public class FoodFragment extends Fragment { } // make it unique - categories = new ArrayList<>(catSet); + ArrayList categories = new ArrayList<>(catSet); categories.add(0, MainApp.gs(R.string.none)); ArrayAdapter adapterCategories = new ArrayAdapter<>(getContext(), @@ -170,7 +160,7 @@ public class FoodFragment extends Fragment { category.setAdapter(adapterCategories); } - void fillSubcategories() { + private void fillSubcategories() { String categoryFilter = category.getSelectedItem().toString(); Set subCatSet = new HashSet<>(); @@ -184,7 +174,7 @@ public class FoodFragment extends Fragment { } // make it unique - subcategories = new ArrayList<>(subCatSet); + ArrayList subcategories = new ArrayList<>(subCatSet); subcategories.add(0, MainApp.gs(R.string.none)); ArrayAdapter adapterSubcategories = new ArrayAdapter<>(getContext(), @@ -192,7 +182,7 @@ public class FoodFragment extends Fragment { subcategory.setAdapter(adapterSubcategories); } - void filterData() { + private void filterData() { String textFilter = filter.getText().toString(); String categoryFilter = category.getSelectedItem().toString(); String subcategoryFilter = subcategory.getSelectedItem().toString(); @@ -227,6 +217,7 @@ public class FoodFragment extends Fragment { this.foodList = foodList; } + @NonNull @Override public FoodsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.food_item, viewGroup, false); @@ -257,7 +248,7 @@ public class FoodFragment extends Fragment { return foodList.size(); } - class FoodsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + class FoodsViewHolder extends RecyclerView.ViewHolder { TextView name; TextView portion; TextView carbs; @@ -269,43 +260,26 @@ public class FoodFragment extends Fragment { FoodsViewHolder(View itemView) { super(itemView); - name = (TextView) itemView.findViewById(R.id.food_name); - portion = (TextView) itemView.findViewById(R.id.food_portion); - carbs = (TextView) itemView.findViewById(R.id.food_carbs); - fat = (TextView) itemView.findViewById(R.id.food_fat); - protein = (TextView) itemView.findViewById(R.id.food_protein); - energy = (TextView) itemView.findViewById(R.id.food_energy); - ns = (TextView) itemView.findViewById(R.id.ns_sign); - remove = (TextView) itemView.findViewById(R.id.food_remove); - remove.setOnClickListener(this); + name = itemView.findViewById(R.id.food_name); + portion = itemView.findViewById(R.id.food_portion); + carbs = itemView.findViewById(R.id.food_carbs); + fat = itemView.findViewById(R.id.food_fat); + protein = itemView.findViewById(R.id.food_protein); + energy = itemView.findViewById(R.id.food_energy); + ns = itemView.findViewById(R.id.ns_sign); + remove = itemView.findViewById(R.id.food_remove); + remove.setOnClickListener(v -> { + final Food food = (Food) v.getTag(); + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.confirmation), MainApp.gs(R.string.removerecord) + "\n" + food.name, (dialog, id) -> { + final String _id = food._id; + if (_id != null && !_id.equals("")) { + NSUpload.removeFoodFromNS(_id); + } + FoodPlugin.getPlugin().getService().delete(food); + }, null); + }); remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); } - - @Override - public void onClick(View v) { - final Food food = (Food) v.getTag(); - switch (v.getId()) { - - case R.id.food_remove: - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + food.name); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - final String _id = food._id; - if (_id != null && !_id.equals("")) { - NSUpload.removeFoodFromNS(_id); - } - FoodPlugin.getPlugin().getService().delete(food); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - - } - } } } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.java index 7a801a44c0..3a776ab541 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.java @@ -2,15 +2,14 @@ package info.nightscout.androidaps.plugins.general.maintenance; import android.Manifest; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Environment; import android.preference.PreferenceManager; -import androidx.core.app.ActivityCompat; -import androidx.fragment.app.Fragment; + import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +29,7 @@ import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.utils.OKDialog; +import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.ToastUtils; /** @@ -38,7 +38,7 @@ import info.nightscout.androidaps.utils.ToastUtils; public class ImportExportPrefs { private static Logger log = LoggerFactory.getLogger(L.CORE); - static File path = new File(Environment.getExternalStorageDirectory().toString()); + private static File path = new File(Environment.getExternalStorageDirectory().toString()); static public final File file = new File(path, MainApp.gs(R.string.app_name) + "Preferences"); private static final int REQUEST_EXTERNAL_STORAGE = 1; @@ -47,21 +47,7 @@ public class ImportExportPrefs { Manifest.permission.WRITE_EXTERNAL_STORAGE }; - public static void verifyStoragePermissions(Activity activity) { - // Check if we have write permission - int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); - - if (permission != PackageManager.PERMISSION_GRANTED) { - // We don't have permission so prompt the user - ActivityCompat.requestPermissions( - activity, - PERMISSIONS_STORAGE, - REQUEST_EXTERNAL_STORAGE - ); - } - } - - public static void verifyStoragePermissions(Fragment fragment) { + static void verifyStoragePermissions(Fragment fragment) { int permission = ContextCompat.checkSelfPermission(fragment.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE); @@ -72,88 +58,71 @@ public class ImportExportPrefs { } - public static void exportSharedPreferences(final Fragment f) { + static void exportSharedPreferences(final Fragment f) { exportSharedPreferences(f.getContext()); } - public static void exportSharedPreferences(final Context c) { - - new AlertDialog.Builder(c) - .setMessage(MainApp.gs(R.string.export_to) + " " + file + " ?") - .setPositiveButton(android.R.string.yes, (dialog, which) -> { - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); - try { - FileWriter fw = new FileWriter(file); - PrintWriter pw = new PrintWriter(fw); - Map prefsMap = prefs.getAll(); - for (Map.Entry entry : prefsMap.entrySet()) { - pw.println(entry.getKey() + "::" + entry.getValue().toString()); - } - pw.close(); - fw.close(); - ToastUtils.showToastInUiThread(c, MainApp.gs(R.string.exported)); - } catch (FileNotFoundException e) { - ToastUtils.showToastInUiThread(c, MainApp.gs(R.string.filenotfound) + " " + file); - log.error("Unhandled exception", e); - } catch (IOException e) { - log.error("Unhandled exception", e); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); + private static void exportSharedPreferences(final Context context) { + OKDialog.showConfirmation(context, MainApp.gs(R.string.maintenance), MainApp.gs(R.string.export_to) + " " + file + " ?", () -> { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + try { + FileWriter fw = new FileWriter(file); + PrintWriter pw = new PrintWriter(fw); + Map prefsMap = prefs.getAll(); + for (Map.Entry entry : prefsMap.entrySet()) { + pw.println(entry.getKey() + "::" + entry.getValue().toString()); + } + pw.close(); + fw.close(); + ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.exported)); + } catch (FileNotFoundException e) { + ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file); + log.error("Unhandled exception", e); + } catch (IOException e) { + log.error("Unhandled exception", e); + } + }); } - public static void importSharedPreferences(final Fragment fragment) { + static void importSharedPreferences(final Fragment fragment) { importSharedPreferences(fragment.getContext()); } public static void importSharedPreferences(final Context context) { - new AlertDialog.Builder(context) - .setMessage(MainApp.gs(R.string.import_from) + " " + file + " ?") - .setPositiveButton(android.R.string.yes, (dialog, which) -> { + OKDialog.showConfirmation(context, MainApp.gs(R.string.maintenance), MainApp.gs(R.string.import_from) + " " + file + " ?", () -> { + String line; + String[] lineParts; + try { + SP.clear(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - String line; - String[] lineParts; - try { - editor.clear(); - editor.commit(); - - BufferedReader reader = new BufferedReader(new FileReader(file)); - while ((line = reader.readLine()) != null) { - lineParts = line.split("::"); - if (lineParts.length == 2) { - if (lineParts[1].equals("true") || lineParts[1].equals("false")) { - editor.putBoolean(lineParts[0], Boolean.parseBoolean(lineParts[1])); - } else { - editor.putString(lineParts[0], lineParts[1]); - } - } + BufferedReader reader = new BufferedReader(new FileReader(file)); + while ((line = reader.readLine()) != null) { + lineParts = line.split("::"); + if (lineParts.length == 2) { + if (lineParts[1].equals("true") || lineParts[1].equals("false")) { + SP.putBoolean(lineParts[0], Boolean.parseBoolean(lineParts[1])); + } else { + SP.putString(lineParts[0], lineParts[1]); } - reader.close(); - editor.commit(); - OKDialog.show(context, MainApp.gs(R.string.setting_imported), MainApp.gs(R.string.restartingapp), () -> { - log.debug("Exiting"); - MainApp.instance().stopKeepAliveService(); - RxBus.INSTANCE.send(new EventAppExit()); - MainApp.closeDbHelper(); - if (context instanceof Activity) { - ((Activity)context).finish(); - } - System.runFinalization(); - System.exit(0); - }); - } catch (FileNotFoundException e) { - ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file); - log.error("Unhandled exception", e); - } catch (IOException e) { - log.error("Unhandled exception", e); } - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); + } + reader.close(); + SP.putBoolean(R.string.key_setupwizard_processed, true); + OKDialog.show(context, MainApp.gs(R.string.setting_imported), MainApp.gs(R.string.restartingapp), () -> { + log.debug("Exiting"); + RxBus.INSTANCE.send(new EventAppExit()); + if (context instanceof Activity) { + ((Activity) context).finish(); + } + System.runFinalization(); + System.exit(0); + }); + } catch (FileNotFoundException e) { + ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file); + log.error("Unhandled exception", e); + } catch (IOException e) { + log.error("Unhandled exception", e); + } + }); } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.java index 6b8863242a..ae1262b651 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.java @@ -2,81 +2,58 @@ package info.nightscout.androidaps.plugins.general.maintenance; import android.content.Intent; import android.os.Bundle; -import androidx.fragment.app.Fragment; -import androidx.appcompat.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.fragment.app.Fragment; + import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.general.food.FoodPlugin; import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.utils.OKDialog; /** * */ public class MaintenanceFragment extends Fragment { - private Fragment f; - - @Override - public void onResume() { - super.onResume(); - - this.f = this; - } - - @Override - public void onPause() { - super.onPause(); - - this.f = null; - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.maintenance_fragment, container, false); - final Fragment f = this; - view.findViewById(R.id.log_send).setOnClickListener(view1 -> MaintenancePlugin.getPlugin().sendLogs()); view.findViewById(R.id.log_delete).setOnClickListener(view1 -> MaintenancePlugin.getPlugin().deleteLogs()); - view.findViewById(R.id.nav_resetdb).setOnClickListener(view1 -> new AlertDialog.Builder(f.getContext()) - .setTitle(R.string.nav_resetdb) - .setMessage(R.string.reset_db_confirm) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { + view.findViewById(R.id.nav_resetdb).setOnClickListener(view1 -> + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.maintenance), MainApp.gs(R.string.reset_db_confirm), () -> { MainApp.getDbHelper().resetDatabases(); // should be handled by Plugin-Interface and // additional service interface and plugin registry FoodPlugin.getPlugin().getService().resetFood(); TreatmentsPlugin.getPlugin().getService().resetTreatments(); }) - .create() - .show()); - + ); view.findViewById(R.id.nav_export).setOnClickListener(view1 -> { // start activity for checking permissions... - ImportExportPrefs.verifyStoragePermissions(f); - ImportExportPrefs.exportSharedPreferences(f); + ImportExportPrefs.verifyStoragePermissions(this); + ImportExportPrefs.exportSharedPreferences(this); }); view.findViewById(R.id.nav_import).setOnClickListener(view1 -> { // start activity for checking permissions... - ImportExportPrefs.verifyStoragePermissions(f); - ImportExportPrefs.importSharedPreferences(f); + ImportExportPrefs.verifyStoragePermissions(this); + ImportExportPrefs.importSharedPreferences(this); }); view.findViewById(R.id.nav_logsettings).setOnClickListener(view1 -> { startActivity(new Intent(getActivity(), LogSettingActivity.class)); }); - return view; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/activities/LogSettingActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/activities/LogSettingActivity.kt index f47bb03513..d9ed0c590c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/activities/LogSettingActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/activities/LogSettingActivity.kt @@ -6,11 +6,11 @@ import android.widget.CheckBox import android.widget.LinearLayout import android.widget.TextView import info.nightscout.androidaps.R -import info.nightscout.androidaps.activities.NoSplashActivity +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.logging.L import kotlinx.android.synthetic.main.activity_logsetting.* -class LogSettingActivity : NoSplashActivity() { +class LogSettingActivity : NoSplashAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.java index 112eb9c951..ea3ee3f9a7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.java @@ -1,9 +1,6 @@ package info.nightscout.androidaps.plugins.general.nsclient; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; import android.text.Html; @@ -25,6 +22,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientN import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -113,20 +111,11 @@ public class NSClientFragment extends Fragment implements View.OnClickListener, NSClientPlugin.getPlugin().clearLog(); break; case R.id.nsclientinternal_clearqueue: - final Context context = getContext(); - AlertDialog.Builder builder = new AlertDialog.Builder(context); - - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage("Clear queue? All data in queue will be lost!"); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - UploadQueue.clearQueue(); - updateGui(); - FabricPrivacy.getInstance().logCustom("NSClientClearQueue"); - } + OKDialog.showConfirmation(getContext(),MainApp.gs(R.string.nsclientinternal), MainApp.gs(R.string.clearqueueconfirm), () -> { + UploadQueue.clearQueue(); + updateGui(); + FabricPrivacy.getInstance().logCustom("NSClientClearQueue"); }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); break; case R.id.nsclientinternal_showqueue: RxBus.INSTANCE.send(new EventNSClientNewLog("QUEUE", NSClientPlugin.getPlugin().queue().textList())); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java index ae5d090211..957d69a935 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java @@ -7,9 +7,12 @@ import android.content.ServiceConnection; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; import android.text.Html; import android.text.Spanned; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +32,8 @@ 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.nsclient.data.AlarmAck; +import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI; @@ -89,7 +94,7 @@ public class NSClientPlugin extends PluginBase { } nsClientReceiverDelegate = - new NsClientReceiverDelegate(MainApp.instance().getApplicationContext()); + new NsClientReceiverDelegate(); } public boolean isAllowed() { @@ -104,7 +109,7 @@ public class NSClientPlugin extends PluginBase { context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); super.onStart(); - nsClientReceiverDelegate.registerReceivers(); + nsClientReceiverDelegate.grabReceiversState(); disposable.add(RxBus.INSTANCE .toObservable(EventNSClientStatus.class) .observeOn(Schedulers.io()) @@ -129,7 +134,6 @@ public class NSClientPlugin extends PluginBase { .subscribe(event -> { if (nsClientService != null) { MainApp.instance().getApplicationContext().unbindService(mConnection); - nsClientReceiverDelegate.unregisterReceivers(); } }, FabricPrivacy::logException) ); @@ -152,11 +156,27 @@ public class NSClientPlugin extends PluginBase { @Override protected void onStop() { MainApp.instance().getApplicationContext().unbindService(mConnection); - nsClientReceiverDelegate.unregisterReceivers(); disposable.clear(); super.onStop(); } + @Override + public void preprocessPreferences(@NotNull PreferenceFragment preferenceFragment) { + super.preprocessPreferences(preferenceFragment); + + if (Config.NSCLIENT) { + PreferenceScreen scrnAdvancedSettings = (PreferenceScreen) preferenceFragment.findPreference(MainApp.gs(R.string.key_advancedsettings)); + if (scrnAdvancedSettings != null) { + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_res_warning))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_res_critical))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_bat_warning))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_bat_critical))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_show_statuslights))); + scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_show_statuslights_extended))); + } + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { @@ -232,4 +252,25 @@ public class NSClientPlugin extends PluginBase { public boolean hasWritePermission() { return nsClientService.hasWriteAuth; } + + public void handleClearAlarm(NSAlarm originalAlarm, long silenceTimeInMsec) { + + if (!isEnabled(PluginType.GENERAL)) { + return; + } + if (SP.getBoolean(R.string.key_ns_noupload, false)) { + if (L.isEnabled(L.NSCLIENT)) + log.debug("Upload disabled. Message dropped"); + return; + } + + AlarmAck ack = new AlarmAck(); + ack.level = originalAlarm.getLevel(); + ack.group = originalAlarm.getGroup(); + ack.silenceTime = silenceTimeInMsec; + + if (nsClientService != null) + nsClientService.sendAlarmAck(ack); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java index 881593b32a..0bc15d1d45 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java @@ -1,14 +1,13 @@ package info.nightscout.androidaps.plugins.general.nsclient; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ResolveInfo; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; + import androidx.annotation.Nullable; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; @@ -24,22 +23,23 @@ import java.util.Locale; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.services.Intents; import info.nightscout.androidaps.data.DetailedBolusInfo; +import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.db.DbRequest; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.aps.loop.APSResult; import info.nightscout.androidaps.plugins.aps.loop.DeviceStatus; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.general.nsclient.data.DbLogger; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.utils.BatteryLevel; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.SP; @@ -53,7 +53,6 @@ public class NSUpload { public static void uploadTempBasalStartAbsolute(TemporaryBasal temporaryBasal, Double originalExtendedAmount) { try { - Context context = MainApp.instance().getApplicationContext(); JSONObject data = new JSONObject(); data.put("eventType", CareportalEvent.TEMPBASAL); data.put("duration", temporaryBasal.durationInMinutes); @@ -65,15 +64,7 @@ public class NSUpload { data.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); if (originalExtendedAmount != null) data.put("originalExtendedAmount", originalExtendedAmount); // for back synchronization - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); } catch (JSONException e) { log.error("Unhandled exception", e); } @@ -96,7 +87,6 @@ public class NSUpload { uploadTempBasalStartAbsolute(t, null); } } else { - Context context = MainApp.instance().getApplicationContext(); JSONObject data = new JSONObject(); data.put("eventType", CareportalEvent.TEMPBASAL); data.put("duration", temporaryBasal.durationInMinutes); @@ -107,15 +97,7 @@ public class NSUpload { data.put("pumpId", temporaryBasal.pumpId); data.put("created_at", DateUtil.toISOString(temporaryBasal.date)); data.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); } } catch (JSONException e) { log.error("Unhandled exception", e); @@ -124,7 +106,6 @@ public class NSUpload { public static void uploadTempBasalEnd(long time, boolean isFakedTempBasal, long pumpId) { try { - Context context = MainApp.instance().getApplicationContext(); JSONObject data = new JSONObject(); data.put("eventType", CareportalEvent.TEMPBASAL); data.put("created_at", DateUtil.toISOString(time)); @@ -133,15 +114,7 @@ public class NSUpload { data.put("isFakedTempBasal", isFakedTempBasal); if (pumpId != 0) data.put("pumpId", pumpId); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); } catch (JSONException e) { log.error("Unhandled exception", e); } @@ -149,7 +122,6 @@ public class NSUpload { public static void uploadExtendedBolus(ExtendedBolus extendedBolus) { try { - Context context = MainApp.instance().getApplicationContext(); JSONObject data = new JSONObject(); data.put("eventType", CareportalEvent.COMBOBOLUS); data.put("duration", extendedBolus.durationInMinutes); @@ -161,15 +133,7 @@ public class NSUpload { data.put("pumpId", extendedBolus.pumpId); data.put("created_at", DateUtil.toISOString(extendedBolus.date)); data.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); } catch (JSONException e) { log.error("Unhandled exception", e); } @@ -177,7 +141,6 @@ public class NSUpload { public static void uploadExtendedBolusEnd(long time, long pumpId) { try { - Context context = MainApp.instance().getApplicationContext(); JSONObject data = new JSONObject(); data.put("eventType", CareportalEvent.COMBOBOLUS); data.put("duration", 0); @@ -189,15 +152,7 @@ public class NSUpload { data.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); if (pumpId != 0) data.put("pumpId", pumpId); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); } catch (JSONException e) { log.error("Unhandled exception", e); } @@ -246,7 +201,12 @@ public class NSUpload { } } else { if (L.isEnabled(L.NSCLIENT)) - log.debug("OpenAPS data too old to upload"); + log.debug("OpenAPS data too old to upload, sending iob only"); + IobTotal[] iob = IobCobCalculatorPlugin.getPlugin().calculateIobArrayInDia(profile); + if (iob.length > 0) { + deviceStatus.iob = iob[0].json(); + deviceStatus.iob.put("time", DateUtil.toISOString(DateUtil.now())); + } } deviceStatus.device = "openaps://" + Build.MANUFACTURER + " " + Build.MODEL; JSONObject pumpstatus = ConfigBuilderPlugin.getPlugin().getActivePump().getJSONStatus(profile, profileName); @@ -258,16 +218,7 @@ public class NSUpload { deviceStatus.uploaderBattery = batteryLevel; deviceStatus.created_at = DateUtil.toISOString(new Date()); - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "devicestatus"); - bundle.putString("data", deviceStatus.mongoRecord().toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, deviceStatus.mongoRecord().toString()); + UploadQueue.add(new DbRequest("dbAdd", "devicestatus", deviceStatus.mongoRecord())); } catch (JSONException e) { log.error("Unhandled exception", e); } @@ -312,21 +263,16 @@ public class NSUpload { public static void uploadTempTarget(TempTarget tempTarget) { try { - Profile profile = ProfileFunctions.getInstance().getProfile(); - - if (profile == null) { - log.error("Profile is null. Skipping upload"); - return; - } - JSONObject data = new JSONObject(); data.put("eventType", CareportalEvent.TEMPORARYTARGET); data.put("duration", tempTarget.durationInMinutes); - data.put("reason", tempTarget.reason); - data.put("targetBottom", Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits())); - data.put("targetTop", Profile.fromMgdlToUnits(tempTarget.high, profile.getUnits())); + if (tempTarget.low > 0) { + data.put("reason", tempTarget.reason); + data.put("targetBottom", Profile.fromMgdlToUnits(tempTarget.low, ProfileFunctions.getSystemUnits())); + data.put("targetTop", Profile.fromMgdlToUnits(tempTarget.high, ProfileFunctions.getSystemUnits())); + data.put("units", ProfileFunctions.getSystemUnits()); + } data.put("created_at", DateUtil.toISOString(tempTarget.date)); - data.put("units", profile.getUnits()); data.put("enteredBy", MainApp.gs(R.string.app_name)); uploadCareportalEntryToNS(data); } catch (JSONException e) { @@ -338,17 +284,7 @@ public class NSUpload { try { JSONObject data = getJson(profileSwitch); if (profileSwitch._id != null) { - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbUpdate"); - bundle.putString("collection", "treatments"); - bundle.putString("data", data.toString()); - bundle.putString("_id", profileSwitch._id); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbUpdate", "treatments", profileSwitch._id, data)); } } catch (JSONException e) { log.error("Unhandled exception", e); @@ -387,16 +323,9 @@ public class NSUpload { prebolus.put("created_at", DateUtil.toISOString(preBolusDate)); uploadCareportalEntryToNS(prebolus); } - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + DbRequest dbr = new DbRequest("dbAdd", "treatments", data); + log.debug("Prepared: " + dbr.log()); + UploadQueue.add(dbr); } catch (Exception e) { log.error("Unhandled exception", e); } @@ -404,40 +333,15 @@ public class NSUpload { } public static void removeCareportalEntryFromNS(String _id) { - try { - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbRemove"); - bundle.putString("collection", "treatments"); - bundle.putString("_id", _id); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbRemove(intent, _id); - } catch (Exception e) { - log.error("Unhandled exception", e); - } - + UploadQueue.add(new DbRequest("dbRemove", "treatments", _id)); } - public static void uploadOpenAPSOffline(double durationInMinutes) { + public static void uploadOpenAPSOffline(CareportalEvent event) { try { - Context context = MainApp.instance().getApplicationContext(); - JSONObject data = new JSONObject(); - data.put("eventType", "OpenAPS Offline"); - data.put("duration", durationInMinutes); - data.put("created_at", DateUtil.toISOString(new Date())); + JSONObject data = new JSONObject(event.json); + data.put("created_at", DateUtil.toISOString(event.date)); data.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); } catch (JSONException e) { log.error("Unhandled exception", e); } @@ -448,10 +352,6 @@ public class NSUpload { } public static void uploadError(String error, Date date) { - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); JSONObject data = new JSONObject(); try { data.put("eventType", "Announcement"); @@ -462,19 +362,10 @@ public class NSUpload { } catch (JSONException e) { log.error("Unhandled exception", e); } - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); } public static void uploadBg(BgReading reading, String source) { - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "entries"); JSONObject data = new JSONObject(); try { data.put("device", source); @@ -486,20 +377,11 @@ public class NSUpload { } catch (JSONException e) { log.error("Unhandled exception", e); } - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "entries", data)); } public static void uploadAppStart() { if (SP.getBoolean(R.string.key_ns_logappstartedevent, true)) { - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); JSONObject data = new JSONObject(); try { data.put("eventType", "Note"); @@ -508,20 +390,17 @@ public class NSUpload { } catch (JSONException e) { log.error("Unhandled exception", e); } - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); + } + } + + public static void uploadProfileStore(JSONObject profileStore) { + if (SP.getBoolean(R.string.key_ns_uploadlocalprofile, false)) { + UploadQueue.add(new DbRequest("dbAdd", "profile", String.valueOf(profileStore))); } } public static void uploadEvent(String careportalEvent, long time, @Nullable String notes) { - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbAdd"); - bundle.putString("collection", "treatments"); JSONObject data = new JSONObject(); try { data.put("eventType", careportalEvent); @@ -533,26 +412,12 @@ public class NSUpload { } catch (JSONException e) { log.error("Unhandled exception", e); } - bundle.putString("data", data.toString()); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbAdd(intent, data.toString()); + UploadQueue.add(new DbRequest("dbAdd", "treatments", data)); } public static void removeFoodFromNS(String _id) { try { - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbRemove"); - bundle.putString("collection", "food"); - bundle.putString("_id", _id); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - DbLogger.dbRemove(intent, _id); + UploadQueue.add(new DbRequest("dbRemove", "food", _id)); } catch (Exception e) { log.error("Unhandled exception", e); } @@ -594,7 +459,7 @@ public class NSUpload { } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java index c68750d828..fd05b133f5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java @@ -18,44 +18,19 @@ import info.nightscout.androidaps.utils.SP; class NsClientReceiverDelegate { - private final Context context; - - private NetworkChangeReceiver networkChangeReceiver = new NetworkChangeReceiver(); - private ChargingStateReceiver chargingStateReceiver = new ChargingStateReceiver(); - private boolean allowedChargingState = true; private boolean allowedNetworkState = true; boolean allowed = true; - NsClientReceiverDelegate(Context context) { - this.context = context; - } - - void registerReceivers() { + void grabReceiversState() { Context context = MainApp.instance().getApplicationContext(); - // register NetworkChangeReceiver --> https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html - // Nougat is not providing Connectivity-Action anymore ;-( - context.registerReceiver(networkChangeReceiver, - new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); - context.registerReceiver(networkChangeReceiver, - new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)); - EventNetworkChange event = networkChangeReceiver.grabNetworkStatus(context); - if (event != null) - RxBus.INSTANCE.send(event); + EventNetworkChange event = NetworkChangeReceiver.grabNetworkStatus(context); + if (event != null) RxBus.INSTANCE.send(event); - context.registerReceiver(chargingStateReceiver, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + EventChargingState eventChargingState = ChargingStateReceiver.grabChargingState(context); + if (eventChargingState != null) RxBus.INSTANCE.send(eventChargingState); - EventChargingState eventChargingState = chargingStateReceiver.grabChargingState(context); - if (eventChargingState != null) - RxBus.INSTANCE.send(eventChargingState); - - } - - void unregisterReceivers() { - context.unregisterReceiver(networkChangeReceiver); - context.unregisterReceiver(chargingStateReceiver); } void onStatusEvent(EventPreferenceChange ev) { @@ -63,11 +38,11 @@ class NsClientReceiverDelegate { ev.isChanged(R.string.key_ns_wifi_ssids) || ev.isChanged(R.string.key_ns_allowroaming) ) { - EventNetworkChange event = networkChangeReceiver.grabNetworkStatus(MainApp.instance().getApplicationContext()); + EventNetworkChange event = NetworkChangeReceiver.grabNetworkStatus(MainApp.instance().getApplicationContext()); if (event != null) RxBus.INSTANCE.send(event); } else if (ev.isChanged(R.string.key_ns_chargingonly)) { - EventChargingState event = chargingStateReceiver.grabChargingState(MainApp.instance().getApplicationContext()); + EventChargingState event = ChargingStateReceiver.grabChargingState(MainApp.instance().getApplicationContext()); if (event != null) RxBus.INSTANCE.send(event); } @@ -91,7 +66,7 @@ class NsClientReceiverDelegate { } } - void processStateChange() { + private void processStateChange() { boolean newAllowedState = allowedChargingState && allowedNetworkState; if (newAllowedState != allowed) { allowed = newAllowedState; @@ -101,7 +76,6 @@ class NsClientReceiverDelegate { boolean calculateStatus(final EventChargingState ev) { boolean chargingOnly = SP.getBoolean(R.string.key_ns_chargingonly, false); - boolean newAllowedState = true; if (!ev.isCharging() && chargingOnly) { @@ -129,8 +103,6 @@ class NsClientReceiverDelegate { } } - return newAllowedState; } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/UploadQueue.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/UploadQueue.java index c2bb0d21ab..f87cee7900 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/UploadQueue.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/UploadQueue.java @@ -14,10 +14,12 @@ import org.slf4j.LoggerFactory; import java.sql.SQLException; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.DbRequest; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService; +import info.nightscout.androidaps.utils.SP; /** * Created by mike on 21.02.2016. @@ -42,21 +44,22 @@ public class UploadQueue { } public static void add(final DbRequest dbr) { + if (SP.getBoolean(R.string.key_ns_noupload, false)) return; startService(); - if (NSClientService.handler != null) { - NSClientService.handler.post(() -> { - if (L.isEnabled(L.NSCLIENT)) - log.debug("Adding to queue: " + dbr.data); - MainApp.getDbHelper().create(dbr); - NSClientPlugin plugin = NSClientPlugin.getPlugin(); - if (plugin != null) { - plugin.resend("newdata"); - } - }); + if (L.isEnabled(L.NSCLIENT)) + log.debug("Adding to queue: " + dbr.log()); + try { + MainApp.getDbHelper().create(dbr); + } catch (Exception e) { + log.error("Unhandled exception", e); + } + NSClientPlugin plugin = NSClientPlugin.getPlugin(); + if (plugin != null) { + plugin.resend("newdata"); } } - public static void clearQueue() { + static void clearQueue() { startService(); if (NSClientService.handler != null) { NSClientService.handler.post(() -> { @@ -104,7 +107,7 @@ public class UploadQueue { } } - public String textList() { + String textList() { String result = ""; CloseableIterator iterator; try { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/broadcasts/BroadcastAckAlarm.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/broadcasts/BroadcastAckAlarm.java deleted file mode 100644 index 7179231068..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/broadcasts/BroadcastAckAlarm.java +++ /dev/null @@ -1,43 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.broadcasts; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.services.Intents; -import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm; -import info.nightscout.androidaps.utils.SP; - -/** - * Created by mike on 11.06.2017. - */ - -public class BroadcastAckAlarm { - - public static void handleClearAlarm(NSAlarm originalAlarm, Context context, long silenceTimeInMsec) { - - Bundle bundle = new Bundle(); - bundle.putInt("level", originalAlarm.getLevel()); - bundle.putString("group", originalAlarm.getGroup()); - bundle.putLong("silenceTime", silenceTimeInMsec); - Intent intent = new Intent(Intents.ACTION_ACK_ALARM); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(MainApp.instance()).sendBroadcast(intent); - - if(SP.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { - bundle = new Bundle(); - bundle.putInt("level", originalAlarm.getLevel()); - bundle.putString("group", originalAlarm.getGroup()); - bundle.putLong("silenceTime", silenceTimeInMsec); - intent = new Intent(Intents.ACTION_ACK_ALARM); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - context.sendBroadcast(intent); - } - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/broadcasts/BroadcastTreatment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/broadcasts/BroadcastTreatment.java index a364d0d4ae..7b722f562d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/broadcasts/BroadcastTreatment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/broadcasts/BroadcastTreatment.java @@ -25,12 +25,11 @@ import info.nightscout.androidaps.utils.SP; public class BroadcastTreatment { private static Logger log = LoggerFactory.getLogger(L.NSCLIENT); - public static void handleNewTreatment(JSONObject treatment, boolean isDelta, boolean isLocalBypass) { + public static void handleNewTreatment(JSONObject treatment, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("treatment", treatment.toString()); bundle.putBoolean("delta", isDelta); - bundle.putBoolean("islocal", isLocalBypass); Intent intent = new Intent(Intents.ACTION_NEW_TREATMENT); intent.putExtras(bundle); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/DbLogger.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/DbLogger.java deleted file mode 100644 index 3aaf4ccac4..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/DbLogger.java +++ /dev/null @@ -1,43 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.data; - -import android.content.Intent; -import android.content.pm.ResolveInfo; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.utils.ToastUtils; - -/** - * Created by mike on 02.07.2016. - */ -public class DbLogger { - private static Logger log = LoggerFactory.getLogger(L.NSCLIENT); - - public static void dbAdd(Intent intent, String data) { - List q = MainApp.instance().getApplicationContext().getPackageManager().queryBroadcastReceivers(intent, 0); - if (q.size() < 1) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.nsclientnotinstalled)); - log.error("DBADD No receivers"); - } else if (L.isEnabled(L.NSCLIENT)) { - if (L.isEnabled(L.NSCLIENT)) - log.debug("DBADD dbAdd " + q.size() + " receivers " + data); - } - } - - public static void dbRemove(Intent intent, String data) { - List q = MainApp.instance().getApplicationContext().getPackageManager().queryBroadcastReceivers(intent, 0); - if (q.size() < 1) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.nsclientnotinstalled)); - log.error("DBREMOVE No receivers"); - } else if (L.isEnabled(L.NSCLIENT)) { - if (L.isEnabled(L.NSCLIENT)) - log.debug("DBREMOVE dbRemove " + q.size() + " receivers " + data); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.java index ea7a8c34bd..89f95608b5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.java @@ -407,7 +407,7 @@ public class NSDeviceStatus { } Uploader uploader = uploaders.get(device); // check if this is new data - if (clock != 0 && (uploader != null && clock > uploader.clock || uploader == null)) { + if (clock != 0 && battery != null && (uploader != null && clock > uploader.clock || uploader == null)) { if (uploader == null) uploader = new Uploader(); uploader.battery = battery; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/AckAlarmReceiver.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/AckAlarmReceiver.java deleted file mode 100644 index 1c46716d69..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/AckAlarmReceiver.java +++ /dev/null @@ -1,60 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.PowerManager; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; -import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck; -import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService; -import info.nightscout.androidaps.utils.SP; - -public class AckAlarmReceiver extends BroadcastReceiver { - private static Logger log = LoggerFactory.getLogger(L.NSCLIENT); - - - @Override - public void onReceive(Context context, Intent intent) { - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - AckAlarmReceiver.class.getSimpleName()); - NSClientPlugin nsClientPlugin = NSClientPlugin.getPlugin(); - if (!nsClientPlugin.isEnabled(PluginType.GENERAL)) { - return; - } - if (SP.getBoolean(R.string.key_ns_noupload, false)) { - if (L.isEnabled(L.NSCLIENT)) - log.debug("Upload disabled. Message dropped"); - return; - } - wakeLock.acquire(); - try { - Bundle bundles = intent.getExtras(); - if (bundles == null) return; - if (!bundles.containsKey("level")) return; - if (!bundles.containsKey("group")) return; - if (!bundles.containsKey("silenceTime")) return; - - AlarmAck ack = new AlarmAck(); - ack.level = bundles.getInt("level"); - ack.group = bundles.getString("group"); - ack.silenceTime = bundles.getLong("silenceTime"); - - NSClientService nsClientService = nsClientPlugin.nsClientService; - if (nsClientService != null) - nsClientService.sendAlarmAck(ack); - - } finally { - wakeLock.release(); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/DBAccessReceiver.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/DBAccessReceiver.java deleted file mode 100644 index 4838c178ef..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/DBAccessReceiver.java +++ /dev/null @@ -1,147 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.PowerManager; - -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.db.DbRequest; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.logging.BundleLogger; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; -import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; -import info.nightscout.androidaps.plugins.general.nsclient.broadcasts.BroadcastTreatment; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.SP; - -public class DBAccessReceiver extends BroadcastReceiver { - private static Logger log = LoggerFactory.getLogger(L.NSCLIENT); - - - @Override - public void onReceive(Context context, Intent intent) { - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - DBAccessReceiver.class.getSimpleName()); - wakeLock.acquire(); - try { - Bundle bundles = intent.getExtras(); - if (bundles == null) return; - if (!bundles.containsKey("action")) return; - - if (L.isEnabled(L.NSCLIENT)) - log.debug(BundleLogger.log(bundles)); - - String collection = null; - String _id = null; - JSONObject data = null; - String action = bundles.getString("action"); - try { - collection = bundles.getString("collection"); - } catch (Exception e) { - log.error("Unhandled exception", e); - return; - } - try { - if (!action.equals("dbAdd")) - _id = bundles.getString("_id"); - } catch (Exception e) { - log.error("Unhandled exception", e); - return; - } - try { - if (!action.equals("dbRemove")) - data = new JSONObject(bundles.getString("data")); - } catch (Exception e) { - log.error("Unhandled exception", e); - return; - } - - if (data == null && !action.equals("dbRemove") || _id == null && action.equals("dbRemove")) { - log.error("DBACCESS no data inside record"); - return; - } - - if (action.equals("dbRemove")) { - data = new JSONObject(); - } - // mark by id - Long nsclientid = System.currentTimeMillis(); - try { - data.put("NSCLIENT_ID", nsclientid); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - - if (!isAllowedCollection(collection)) { - log.error("DBACCESS wrong collection specified"); - return; - } - - if (action.equals("dbRemove")) { - if (shouldUpload()) { - DbRequest dbr = new DbRequest(action, collection, nsclientid.toString(), _id); - UploadQueue.add(dbr); - } - } else if (action.equals("dbUpdate")) { - if (shouldUpload()) { - DbRequest dbr = new DbRequest(action, collection, nsclientid.toString(), _id, data); - UploadQueue.add(dbr); - } - } else { - DbRequest dbr = new DbRequest(action, collection, nsclientid.toString(), data); - // this is not used as mongo _id but only for searching in UploadQueue database - // if record has to be removed from queue before upload - dbr._id = nsclientid.toString(); - - if (shouldUpload()) { - UploadQueue.add(dbr); - } - if (collection.equals("treatments")) { - generateTreatmentOfflineBroadcast(dbr); - } - } - - } finally { - wakeLock.release(); - } - - } - - public boolean shouldUpload() { - NSClientPlugin nsClientPlugin = NSClientPlugin.getPlugin(); - return nsClientPlugin.isEnabled(PluginType.GENERAL) && !SP.getBoolean(R.string.key_ns_noupload, false); - } - - public void generateTreatmentOfflineBroadcast(DbRequest request) { - if (request.action.equals("dbAdd")) { - try { - JSONObject data = new JSONObject(request.data); - data.put("mills", DateUtil.fromISODateString(data.getString("created_at")).getTime()); - data.put("_id", data.get("NSCLIENT_ID")); // this is only fake id - BroadcastTreatment.handleNewTreatment(data, false, true); - } catch (Exception e) { - log.error("Unhadled exception", e); - } - } - } - - private boolean isAllowedCollection(String collection) { - // "treatments" || "entries" || "devicestatus" || "profile" || "food" - if (collection.equals("treatments")) return true; - if (collection.equals("entries")) return true; - if (collection.equals("devicestatus")) return true; - if (collection.equals("profile")) return true; - if (collection.equals("food")) return true; - return false; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java index 5c2a7cac84..0976755f06 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java @@ -7,7 +7,6 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.graphics.Color; import android.graphics.Paint; import android.os.Bundle; import android.os.Handler; @@ -25,8 +24,8 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.PopupMenu; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.res.ResourcesCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; @@ -61,6 +60,14 @@ import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.dialogs.CalibrationDialog; +import info.nightscout.androidaps.dialogs.CarbsDialog; +import info.nightscout.androidaps.dialogs.InsulinDialog; +import info.nightscout.androidaps.dialogs.ProfileSwitchDialog; +import info.nightscout.androidaps.dialogs.ProfileViewerDialog; +import info.nightscout.androidaps.dialogs.TempTargetDialog; +import info.nightscout.androidaps.dialogs.TreatmentDialog; +import info.nightscout.androidaps.dialogs.WizardDialog; import info.nightscout.androidaps.events.EventAcceptOpenLoopChange; import info.nightscout.androidaps.events.EventCareportalEventChange; import info.nightscout.androidaps.events.EventExtendedBolusChange; @@ -84,16 +91,9 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment; -import info.nightscout.androidaps.plugins.general.careportal.Dialogs.NewNSTreatmentDialog; -import info.nightscout.androidaps.plugins.general.careportal.OptionsToShow; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity; -import info.nightscout.androidaps.plugins.general.overview.dialogs.CalibrationDialog; -import info.nightscout.androidaps.plugins.general.overview.dialogs.NewCarbsDialog; -import info.nightscout.androidaps.plugins.general.overview.dialogs.NewInsulinDialog; -import info.nightscout.androidaps.plugins.general.overview.dialogs.NewTreatmentDialog; -import info.nightscout.androidaps.plugins.general.overview.dialogs.WizardDialog; import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData; import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; @@ -105,7 +105,6 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCa import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin; import info.nightscout.androidaps.plugins.source.SourceXdripPlugin; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.plugins.treatments.fragments.ProfileViewerDialog; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.BolusWizard; import info.nightscout.androidaps.utils.DateUtil; @@ -120,6 +119,7 @@ import info.nightscout.androidaps.utils.T; import info.nightscout.androidaps.utils.ToastUtils; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; import static info.nightscout.androidaps.utils.DateUtil.now; @@ -149,7 +149,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, TextView openapsDeviceStatusView; TextView uploaderDeviceStatusView; TextView iobCalculationProgressView; - LinearLayout loopStatusLayout; + ConstraintLayout loopStatusLayout; LinearLayout pumpStatusLayout; GraphView bgGraph; GraphView iobGraph; @@ -170,7 +170,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, RecyclerView notificationsView; LinearLayoutManager llm; - LinearLayout acceptTempLayout; SingleClickButton acceptTempButton; SingleClickButton treatmentButton; @@ -222,8 +221,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } else if (Config.NSCLIENT) { view = inflater.inflate(R.layout.overview_fragment_nsclient, container, false); shorttextmode = true; - } else if (smallHeight || landscape) { - view = inflater.inflate(R.layout.overview_fragment_smallheight, container, false); + } else if (smallHeight || landscape) { // now testing the same layout for small displays as well + view = inflater.inflate(R.layout.overview_fragment, container, false); } else { view = inflater.inflate(R.layout.overview_fragment, container, false); } @@ -248,7 +247,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, openapsDeviceStatusView = (TextView) view.findViewById(R.id.overview_openaps); uploaderDeviceStatusView = (TextView) view.findViewById(R.id.overview_uploader); iobCalculationProgressView = (TextView) view.findViewById(R.id.overview_iobcalculationprogess); - loopStatusLayout = (LinearLayout) view.findViewById(R.id.overview_looplayout); + loopStatusLayout = view.findViewById(R.id.overview_looplayout); pumpStatusLayout = (LinearLayout) view.findViewById(R.id.overview_pumpstatuslayout); pumpStatusView.setBackgroundColor(MainApp.gc(R.color.colorInitializingBorder)); @@ -296,8 +295,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (cgmButton != null) cgmButton.setOnClickListener(this); - acceptTempLayout = (LinearLayout) view.findViewById(R.id.overview_accepttemplayout); - notificationsView = (RecyclerView) view.findViewById(R.id.overview_notifications); notificationsView.setHasFixedSize(false); llm = new LinearLayoutManager(view.getContext()); @@ -358,80 +355,80 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, super.onResume(); disposable.add(RxBus.INSTANCE .toObservable(EventRefreshOverview.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(eventOpenAPSUpdateGui -> scheduleUpdateGUI(eventOpenAPSUpdateGui.getFrom()), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventExtendedBolusChange.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventExtendedBolusChange"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventTempBasalChange.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventTempBasalChange"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventTreatmentChange.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventTreatmentChange"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventTempTargetChange.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventTempTargetChange"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventAcceptOpenLoopChange.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventAcceptOpenLoopChange"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventCareportalEventChange.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventCareportalEventChange"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventInitializationChanged.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventInitializationChanged"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventAutosensCalculationFinished.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventAutosensCalculationFinished"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventProfileNeedsUpdate.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventProfileNeedsUpdate"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventPreferenceChange.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventPreferenceChange"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventNewOpenLoopNotification.class) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventNewOpenLoopNotification"), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventPumpStatusChanged.class) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> updatePumpStatus(event.getStatus()), + .subscribe(event -> updatePumpStatus(event), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE @@ -670,6 +667,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, return true; final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); if (item.getTitle().equals(MainApp.gs(R.string.disableloop))) { + log.debug("USER ENTRY: LOOP DISABLED"); loopPlugin.setPluginEnabled(PluginType.LOOP, false); loopPlugin.setFragmentVisible(PluginType.LOOP, false); ConfigBuilderPlugin.getPlugin().storeSettings("DisablingLoop"); @@ -682,17 +680,19 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } } }); - NSUpload.uploadOpenAPSOffline(24 * 60); // upload 24h, we don't know real duration + LoopPlugin.getPlugin().createOfflineEvent(24 * 60); // upload 24h, we don't know real duration return true; } else if (item.getTitle().equals(MainApp.gs(R.string.enableloop))) { + log.debug("USER ENTRY: LOOP ENABLED"); loopPlugin.setPluginEnabled(PluginType.LOOP, true); loopPlugin.setFragmentVisible(PluginType.LOOP, true); ConfigBuilderPlugin.getPlugin().storeSettings("EnablingLoop"); updateGUI("suspendmenu"); - NSUpload.uploadOpenAPSOffline(0); + LoopPlugin.getPlugin().createOfflineEvent(0); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.resume)) || item.getTitle().equals(MainApp.gs(R.string.reconnect))) { + log.debug("USER ENTRY: RESUME"); loopPlugin.suspendTo(0L); updateGUI("suspendmenu"); ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() { @@ -704,51 +704,58 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } }); SP.putBoolean(R.string.key_objectiveusereconnect, true); - NSUpload.uploadOpenAPSOffline(0); + LoopPlugin.getPlugin().createOfflineEvent(0); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.suspendloopfor1h))) { + log.debug("USER ENTRY: SUSPEND 1h"); LoopPlugin.getPlugin().suspendLoop(60); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.suspendloopfor2h))) { + log.debug("USER ENTRY: SUSPEND 2h"); LoopPlugin.getPlugin().suspendLoop(120); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.suspendloopfor3h))) { + log.debug("USER ENTRY: SUSPEND 3h"); LoopPlugin.getPlugin().suspendLoop(180); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.suspendloopfor10h))) { + log.debug("USER ENTRY: SUSPEND 10h"); LoopPlugin.getPlugin().suspendLoop(600); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.disconnectpumpfor15m))) { + log.debug("USER ENTRY: DISCONNECT 15m"); LoopPlugin.getPlugin().disconnectPump(15, profile); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.disconnectpumpfor30m))) { + log.debug("USER ENTRY: DISCONNECT 30m"); LoopPlugin.getPlugin().disconnectPump(30, profile); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.disconnectpumpfor1h))) { + log.debug("USER ENTRY: DISCONNECT 1h"); LoopPlugin.getPlugin().disconnectPump(60, profile); SP.putBoolean(R.string.key_objectiveusedisconnect, true); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.disconnectpumpfor2h))) { + log.debug("USER ENTRY: DISCONNECT 2h"); LoopPlugin.getPlugin().disconnectPump(120, profile); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.disconnectpumpfor3h))) { + log.debug("USER ENTRY: DISCONNECT 3h"); LoopPlugin.getPlugin().disconnectPump(180, profile); updateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.gs(R.string.careportal_profileswitch))) { - NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); - final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCHDIRECT; - profileswitch.executeProfileSwitch = true; - newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); - newDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); + FragmentManager manager = getFragmentManager(); + if (manager != null) + new ProfileSwitchDialog().show(manager, "Overview"); } else if (item.getTitle().equals(MainApp.gs(R.string.danar_viewprofile))) { Bundle args = new Bundle(); args.putLong("time", DateUtil.now()); @@ -759,45 +766,44 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (manager != null) pvd.show(manager, "ProfileViewDialog"); } else if (item.getTitle().equals(MainApp.gs(R.string.eatingsoon))) { - DefaultValueHelper defHelper = new DefaultValueHelper(); - double target = defHelper.determineEatingSoonTT(profile.getUnits()); + log.debug("USER ENTRY: TEMP TARGET EATING SOON"); + double target = Profile.toMgdl(DefaultValueHelper.determineEatingSoonTT(), ProfileFunctions.getSystemUnits()); TempTarget tempTarget = new TempTarget() .date(System.currentTimeMillis()) - .duration(defHelper.determineEatingSoonTTDuration()) + .duration(DefaultValueHelper.determineEatingSoonTTDuration()) .reason(MainApp.gs(R.string.eatingsoon)) .source(Source.USER) - .low(Profile.toMgdl(target, profile.getUnits())) - .high(Profile.toMgdl(target, profile.getUnits())); + .low(target) + .high(target); TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } else if (item.getTitle().equals(MainApp.gs(R.string.activity))) { - DefaultValueHelper defHelper = new DefaultValueHelper(); - double target = defHelper.determineActivityTT(profile.getUnits()); + log.debug("USER ENTRY: TEMP TARGET ACTIVITY"); + double target = Profile.toMgdl(DefaultValueHelper.determineActivityTT(), ProfileFunctions.getSystemUnits()); TempTarget tempTarget = new TempTarget() .date(now()) - .duration(defHelper.determineActivityTTDuration()) + .duration(DefaultValueHelper.determineActivityTTDuration()) .reason(MainApp.gs(R.string.activity)) .source(Source.USER) - .low(Profile.toMgdl(target, profile.getUnits())) - .high(Profile.toMgdl(target, profile.getUnits())); + .low(target) + .high(target); TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } else if (item.getTitle().equals(MainApp.gs(R.string.hypo))) { - DefaultValueHelper defHelper = new DefaultValueHelper(); - double target = defHelper.determineHypoTT(profile.getUnits()); + log.debug("USER ENTRY: TEMP TARGET HYPO"); + double target = Profile.toMgdl(DefaultValueHelper.determineHypoTT(), ProfileFunctions.getSystemUnits()); TempTarget tempTarget = new TempTarget() .date(now()) - .duration(defHelper.determineHypoTTDuration()) + .duration(DefaultValueHelper.determineHypoTTDuration()) .reason(MainApp.gs(R.string.hypo)) .source(Source.USER) - .low(Profile.toMgdl(target, profile.getUnits())) - .high(Profile.toMgdl(target, profile.getUnits())); + .low(target) + .high(target); TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } else if (item.getTitle().equals(MainApp.gs(R.string.custom))) { - NewNSTreatmentDialog newTTDialog = new NewNSTreatmentDialog(); - final OptionsToShow temptarget = CareportalFragment.TEMPTARGET; - temptarget.executeTempTarget = true; - newTTDialog.setOptions(temptarget, R.string.careportal_temporarytarget); - newTTDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); + FragmentManager manager = getFragmentManager(); + if (manager != null) + new TempTargetDialog().show(manager, "Overview"); } else if (item.getTitle().equals(MainApp.gs(R.string.cancel))) { + log.debug("USER ENTRY: TEMP TARGET CANCEL"); TempTarget tempTarget = new TempTarget() .source(Source.USER) .date(now()) @@ -814,12 +820,11 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, public void onClick(View v) { boolean xdrip = SourceXdripPlugin.getPlugin().isEnabled(PluginType.BGSOURCE); boolean dexcom = SourceDexcomPlugin.INSTANCE.isEnabled(PluginType.BGSOURCE); - String units = ProfileFunctions.getInstance().getProfileUnits(); FragmentManager manager = getFragmentManager(); // try to fix https://fabric.io/nightscout3/android/apps/info.nightscout.androidaps/issues/5aca7a1536c7b23527eb4be7?time=last-seven-days // https://stackoverflow.com/questions/14860239/checking-if-state-is-saved-before-committing-a-fragmenttransaction - if (manager.isStateSaved()) + if (manager == null || manager.isStateSaved()) return; switch (v.getId()) { case R.id.overview_accepttempbutton: @@ -864,14 +869,13 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } break; case R.id.overview_treatmentbutton: - NewTreatmentDialog treatmentDialogFragment = new NewTreatmentDialog(); - treatmentDialogFragment.show(manager, "TreatmentDialog"); + new TreatmentDialog().show(manager, "Overview"); break; case R.id.overview_insulinbutton: - new NewInsulinDialog().show(manager, "InsulinDialog"); + new InsulinDialog().show(manager, "Overview"); break; case R.id.overview_carbsbutton: - new NewCarbsDialog().show(manager, "CarbsDialog"); + new CarbsDialog().show(manager, "Overview"); break; case R.id.overview_pumpstatus: if (ConfigBuilderPlugin.getPlugin().getActivePump().isSuspended() || !ConfigBuilderPlugin.getPlugin().getActivePump().isInitialized()) @@ -892,10 +896,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, getContext().startActivity(intent); return true; } catch (ActivityNotFoundException e) { - new AlertDialog.Builder(getContext()) - .setMessage(R.string.error_starting_cgm) - .setPositiveButton("OK", null) - .show(); + OKDialog.show(getContext(), "", MainApp.gs(R.string.error_starting_cgm)); return false; } } @@ -921,16 +922,12 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, LoopPlugin.getPlugin().invoke("Accept temp button", false); final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; if (finalLastRun != null && finalLastRun.lastAPSRun != null && finalLastRun.constraintsProcessed.isChangeRequested()) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.setbasalquestion) + "\n" + finalLastRun.constraintsProcessed); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + OKDialog.showConfirmation(context, MainApp.gs(R.string.pump_tempbasal_label), finalLastRun.constraintsProcessed.toSpanned(), () -> { + log.debug("USER ENTRY: ACCEPT TEMP BASAL"); hideTempRecommendation(); clearNotification(); LoopPlugin.getPlugin().acceptChangeRequest(); }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); } } } @@ -950,7 +947,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, Integer carbsAfterConstraints = MainApp.getConstraintChecker().applyCarbsConstraints(new Constraint<>(quickWizardEntry.carbs())).value(); if (Math.abs(wizard.getInsulinAfterConstraints() - wizard.getCalculatedTotalInsulin()) >= pump.getPumpDescription().pumpType.determineCorrectBolusStepSize(wizard.getInsulinAfterConstraints()) || !carbsAfterConstraints.equals(quickWizardEntry.carbs())) { - OKDialog.show(getContext(), MainApp.gs(R.string.treatmentdeliveryerror), MainApp.gs(R.string.constraints_violation) + "\n" + MainApp.gs(R.string.changeyourinput), null); + OKDialog.show(getContext(), MainApp.gs(R.string.treatmentdeliveryerror), MainApp.gs(R.string.constraints_violation) + "\n" + MainApp.gs(R.string.changeyourinput)); return; } @@ -963,8 +960,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(() -> { - if (acceptTempLayout != null) - acceptTempLayout.setVisibility(View.GONE); + if (acceptTempButton != null) + acceptTempButton.setVisibility(View.GONE); }); } @@ -976,7 +973,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, ActionStringHandler.handleInitiate("cancelChangeRequest"); } - private void updatePumpStatus(String status) { + private void updatePumpStatus(EventPumpStatusChanged event) { + String status = event.getStatus(); if (!status.equals("")) { pumpStatusView.setText(status); pumpStatusLayout.setVisibility(View.VISIBLE); @@ -1041,9 +1039,9 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, final Profile profile = ProfileFunctions.getInstance().getProfile(); final String profileName = ProfileFunctions.getInstance().getProfileName(); - final String units = profile.getUnits(); - final double lowLine = OverviewPlugin.INSTANCE.determineLowLine(units); - final double highLine = OverviewPlugin.INSTANCE.determineHighLine(units); + final String units = ProfileFunctions.getSystemUnits(); + final double lowLine = OverviewPlugin.INSTANCE.determineLowLine(); + final double highLine = OverviewPlugin.INSTANCE.determineHighLine(); //Start with updating the BG as it is unaffected by loop. // **** BG value **** @@ -1064,8 +1062,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (deltaShortView != null) deltaShortView.setText(Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)); if (avgdeltaView != null) - avgdeltaView.setText("øΔ15m: " + Profile.toUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units) + - " øΔ40m: " + Profile.toUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)); + avgdeltaView.setText("øΔ15m: " + Profile.toUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units) + "\n" + + "øΔ40m: " + Profile.toUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)); } else { if (deltaView != null) deltaView.setText("Δ " + MainApp.gs(R.string.notavailable)); @@ -1121,27 +1119,25 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (tempTarget != null) { tempTargetView.setTextColor(MainApp.gc(R.color.ribbonTextWarning)); tempTargetView.setBackgroundColor(MainApp.gc(R.color.ribbonWarning)); - tempTargetView.setVisibility(View.VISIBLE); tempTargetView.setText(Profile.toTargetRangeString(tempTarget.low, tempTarget.high, Constants.MGDL, units) + " " + DateUtil.untilString(tempTarget.end())); } else { tempTargetView.setTextColor(MainApp.gc(R.color.ribbonTextDefault)); tempTargetView.setBackgroundColor(MainApp.gc(R.color.ribbonDefault)); - tempTargetView.setText(Profile.toTargetRangeString(profile.getTargetLow(), profile.getTargetHigh(), units, units)); - tempTargetView.setVisibility(View.VISIBLE); + tempTargetView.setText(Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), Constants.MGDL, units)); } // **** Temp button **** - if (acceptTempLayout != null) { + if (acceptTempButton != null) { boolean showAcceptButton = !closedLoopEnabled.value(); // Open mode needed showAcceptButton = showAcceptButton && finalLastRun != null && finalLastRun.lastAPSRun != null; // aps result must exist - showAcceptButton = showAcceptButton && (finalLastRun.lastOpenModeAccept == null || finalLastRun.lastOpenModeAccept.getTime() < finalLastRun.lastAPSRun.getTime()); // never accepted or before last result + showAcceptButton = showAcceptButton && (finalLastRun.lastOpenModeAccept == 0 || finalLastRun.lastOpenModeAccept < finalLastRun.lastAPSRun.getTime()); // never accepted or before last result showAcceptButton = showAcceptButton && finalLastRun.constraintsProcessed.isChangeRequested(); // change is requested if (showAcceptButton && pump.isInitialized() && !pump.isSuspended() && LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) { - acceptTempLayout.setVisibility(View.VISIBLE); + acceptTempButton.setVisibility(View.VISIBLE); acceptTempButton.setText(MainApp.gs(R.string.setbasalquestion) + "\n" + finalLastRun.constraintsProcessed); } else { - acceptTempLayout.setVisibility(View.GONE); + acceptTempButton.setVisibility(View.GONE); } } @@ -1172,58 +1168,44 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (activeTemp != null) { basalText = "T: " + activeTemp.toStringVeryShort(); } else { - basalText = DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h"; + basalText = MainApp.gs(R.string.pump_basebasalrate, profile.getBasal()); } - baseBasalView.setOnClickListener(v -> { - String fullText = MainApp.gs(R.string.pump_basebasalrate_label) + ": " + DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h\n"; - if (activeTemp != null) { - fullText += MainApp.gs(R.string.pump_tempbasal_label) + ": " + activeTemp.toStringFull(); - } - OKDialog.show(getActivity(), MainApp.gs(R.string.basal), fullText, null); - }); - } else { if (activeTemp != null) { - basalText = activeTemp.toStringFull() + " "; - } - if (Config.NSCLIENT) - basalText += "(" + DecimalFormatter.to2Decimal(profile.getBasal()) + " U/h)"; - else if (pump.getPumpDescription().isTempBasalCapable) { - basalText += "(" + DecimalFormatter.to2Decimal(pump.getBaseBasalRate()) + "U/h)"; + basalText = activeTemp.toStringFull(); + } else { + basalText = MainApp.gs(R.string.pump_basebasalrate, profile.getBasal()); } } + baseBasalView.setText(basalText); + baseBasalView.setOnClickListener(v -> { + String fullText = MainApp.gs(R.string.pump_basebasalrate_label) + ": " + MainApp.gs(R.string.pump_basebasalrate, profile.getBasal()) + "\n"; + if (activeTemp != null) { + fullText += MainApp.gs(R.string.pump_tempbasal_label) + ": " + activeTemp.toStringFull(); + } + OKDialog.show(getActivity(), MainApp.gs(R.string.basal), fullText); + }); + if (activeTemp != null) { baseBasalView.setTextColor(MainApp.gc(R.color.basal)); } else { - baseBasalView.setTextColor(Color.WHITE); - + baseBasalView.setTextColor(MainApp.gc(R.color.defaulttextcolor)); } - baseBasalView.setText(basalText); final ExtendedBolus extendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis()); String extendedBolusText = ""; if (extendedBolusView != null) { // must not exists in all layouts - if (shorttextmode) { - if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses()) { - extendedBolusText = DecimalFormatter.to2Decimal(extendedBolus.absoluteRate()) + "U/h"; - } - } else { - if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses()) { - extendedBolusText = extendedBolus.toString(); - } - } + if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses()) + extendedBolusText = shorttextmode ? DecimalFormatter.to2Decimal(extendedBolus.absoluteRate()) + "U/h" : extendedBolus.toStringMedium(); extendedBolusView.setText(extendedBolusText); - if (Config.NSCLIENT) { - extendedBolusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.extendedbolus), extendedBolus.toString(), null)); - } - if (extendedBolusText.equals("")) - extendedBolusView.setVisibility(Config.NSCLIENT ? View.INVISIBLE : View.GONE); - else - extendedBolusView.setVisibility(View.VISIBLE); + extendedBolusView.setOnClickListener(v -> { + if (extendedBolus != null) + OKDialog.show(getActivity(), MainApp.gs(R.string.extended_bolus), extendedBolus.toString()); + }); } - activeProfileView.setText(ProfileFunctions.getInstance().getProfileName()); + activeProfileView.setText(ProfileFunctions.getInstance().getProfileNameWithDuration()); if (profile.getPercentage() != 100 || profile.getTimeshift() != 0) { activeProfileView.setBackgroundColor(MainApp.gc(R.color.ribbonWarning)); activeProfileView.setTextColor(MainApp.gc(R.color.ribbonTextWarning)); @@ -1309,7 +1291,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, String iobtext1 = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U\n" + MainApp.gs(R.string.bolus) + ": " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U\n" + MainApp.gs(R.string.basal) + ": " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U\n"; - OKDialog.show(getActivity(), MainApp.gs(R.string.iob), iobtext1, null); + OKDialog.show(getActivity(), MainApp.gs(R.string.iob), iobtext1); }); } else if (MainApp.sResources.getBoolean(R.bool.isTablet)) { String iobtext = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" @@ -1361,19 +1343,19 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, // pump status from ns if (pumpDeviceStatusView != null) { pumpDeviceStatusView.setText(NSDeviceStatus.getInstance().getPumpStatus()); - pumpDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.pump), NSDeviceStatus.getInstance().getExtendedPumpStatus(), null)); + pumpDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.pump), NSDeviceStatus.getInstance().getExtendedPumpStatus())); } // OpenAPS status from ns if (openapsDeviceStatusView != null) { openapsDeviceStatusView.setText(NSDeviceStatus.getInstance().getOpenApsStatus()); - openapsDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.openaps), NSDeviceStatus.getInstance().getExtendedOpenApsStatus(), null)); + openapsDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.openaps), NSDeviceStatus.getInstance().getExtendedOpenApsStatus())); } // Uploader status from ns if (uploaderDeviceStatusView != null) { uploaderDeviceStatusView.setText(NSDeviceStatus.getInstance().getUploaderStatusSpanned()); - uploaderDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.uploader), NSDeviceStatus.getInstance().getExtendedUploaderStatus(), null)); + uploaderDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.uploader), NSDeviceStatus.getInstance().getExtendedUploaderStatus())); } // Sensitivity diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt index cece359f22..8e4a9581b8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.general.overview -import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.events.EventRefreshOverview @@ -9,7 +9,6 @@ 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.configBuilder.ProfileFunctions import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore @@ -65,22 +64,17 @@ object OverviewPlugin : PluginBase(PluginDescription() super.onStop() } - fun determineHighLine(units: String): Double { - var highLineSetting = SP.getDouble("high_mark", Profile.fromMgdlToUnits(bgTargetHigh, units))!! - if (highLineSetting < 1) - highLineSetting = Profile.fromMgdlToUnits(180.0, units) + fun determineHighLine(): Double { + var highLineSetting = SP.getDouble(R.string.key_high_mark, bgTargetHigh) + if (highLineSetting < 1) highLineSetting = Constants.HIGHMARK + highLineSetting = Profile.toCurrentUnits(highLineSetting) return highLineSetting } fun determineLowLine(): Double { - val profile = ProfileFunctions.getInstance().profile ?: return bgTargetLow - return determineLowLine(profile.units) - } - - fun determineLowLine(units: String): Double { - var lowLineSetting = SP.getDouble("low_mark", Profile.fromMgdlToUnits(bgTargetLow, units))!! - if (lowLineSetting < 1) - lowLineSetting = Profile.fromMgdlToUnits(76.0, units) + var lowLineSetting = SP.getDouble(R.string.key_low_mark, bgTargetLow) + if (lowLineSetting < 1) lowLineSetting = Constants.LOWMARK + lowLineSetting = Profile.toCurrentUnits(lowLineSetting) return lowLineSetting } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatuslightHandler.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatuslightHandler.java index 23c3fd2cec..557ffd8d73 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatuslightHandler.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatuslightHandler.java @@ -12,6 +12,7 @@ import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment; import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus; +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.SetWarnColor; @@ -34,10 +35,14 @@ class StatuslightHandler { applyStatuslight("sage", CareportalEvent.SENSORCHANGE, sageView, "SEN", 164, 166); - double batteryLevel = pump.isInitialized() ? pump.getBatteryLevel() : -1; - applyStatuslightLevel(R.string.key_statuslights_bat_critical, 5.0, - R.string.key_statuslights_bat_warning, 22.0, - batteryView, "BAT", batteryLevel); + if (pump.model() != PumpType.AccuChekCombo) { + double batteryLevel = pump.isInitialized() ? pump.getBatteryLevel() : -1; + applyStatuslightLevel(R.string.key_statuslights_bat_critical, 5.0, + R.string.key_statuslights_bat_warning, 22.0, + batteryView, "BAT", batteryLevel); + } else { + applyStatuslight("bage", CareportalEvent.PUMPBATTERYCHANGE, batteryView, "BAT", 224, 336); + } } @@ -105,13 +110,18 @@ class StatuslightHandler { handleAge("sage", CareportalEvent.SENSORCHANGE, sageView, "SEN ", 164, 166); - handleLevel(R.string.key_statuslights_bat_critical, 26.0, - R.string.key_statuslights_bat_warning, 51.0, - batteryView, "BAT ", pump.getBatteryLevel()); + if (pump.model() != PumpType.AccuChekCombo) { + handleLevel(R.string.key_statuslights_bat_critical, 26.0, + R.string.key_statuslights_bat_warning, 51.0, + batteryView, "BAT ", pump.getBatteryLevel()); + } else { + handleAge("bage", CareportalEvent.PUMPBATTERYCHANGE, batteryView, "BAT ", + 224, 336); + } } void handleAge(String nsSettingPlugin, String eventName, TextView view, String text, - int defaultUrgentThreshold, int defaultWarnThreshold) { + int defaultWarnThreshold, int defaultUrgentThreshold) { NSSettingsStatus nsSettings = new NSSettingsStatus().getInstance(); if (view != null) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/BolusProgressDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/BolusProgressDialog.java deleted file mode 100644 index 645f6d2b19..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/BolusProgressDialog.java +++ /dev/null @@ -1,198 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs; - - -import android.app.Activity; -import android.os.Bundle; -import android.os.SystemClock; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.ProgressBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.fragment.app.DialogFragment; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning; -import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; -import info.nightscout.androidaps.utils.FabricPrivacy; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -public class BolusProgressDialog extends DialogFragment implements View.OnClickListener { - private static Logger log = LoggerFactory.getLogger(L.UI); - private CompositeDisposable disposable = new CompositeDisposable(); - - Button stopButton; - TextView statusView; - TextView stopPressedView; - ProgressBar progressBar; - BolusProgressHelperActivity helperActivity; - - static double amount; - public static boolean bolusEnded = false; - public static boolean running = true; - public static boolean stopPressed = false; - - private String state; - private final static String DEFAULT_STATE = MainApp.gs(R.string.waitingforpump); - - public BolusProgressDialog() { - super(); - } - - public void setInsulin(double amount) { - BolusProgressDialog.amount = amount; - bolusEnded = false; - } - - public void setHelperActivity(BolusProgressHelperActivity activity) { - this.helperActivity = activity; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - getDialog().setTitle(String.format(MainApp.gs(R.string.overview_bolusprogress_goingtodeliver), amount)); - View view = inflater.inflate(R.layout.overview_bolusprogress_dialog, container, false); - stopButton = view.findViewById(R.id.overview_bolusprogress_stop); - statusView = view.findViewById(R.id.overview_bolusprogress_status); - stopPressedView = view.findViewById(R.id.overview_bolusprogress_stoppressed); - progressBar = view.findViewById(R.id.overview_bolusprogress_progressbar); - stopButton.setOnClickListener(this); - progressBar.setMax(100); - state = savedInstanceState != null ? savedInstanceState.getString("state", DEFAULT_STATE) : DEFAULT_STATE; - statusView.setText(state); - setCancelable(false); - stopPressed = false; - return view; - } - - @Override - public void onResume() { - super.onResume(); - if (L.isEnabled(L.UI)) - log.debug("onResume"); - if (!ConfigBuilderPlugin.getPlugin().getCommandQueue().bolusInQueue()) { - bolusEnded = true; - } - if (bolusEnded) { - dismiss(); - } else { - if (getDialog() != null) - getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - running = true; - if (L.isEnabled(L.UI)) - log.debug("onResume running"); - } - disposable.add(RxBus.INSTANCE - .toObservable(EventPumpStatusChanged.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> statusView.setText(event.getStatus()), FabricPrivacy::logException) - ); - disposable.add(RxBus.INSTANCE - .toObservable(EventDismissBolusProgressIfRunning.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> { - if (L.isEnabled(L.UI)) log.debug("EventDismissBolusProgressIfRunning"); - if (BolusProgressDialog.running) dismiss(); - }, FabricPrivacy::logException) - ); - disposable.add(RxBus.INSTANCE - .toObservable(EventOverviewBolusProgress.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> { - if (L.isEnabled(L.UI)) - log.debug("Status: " + event.getStatus() + " Percent: " + event.getPercent()); - statusView.setText(event.getStatus()); - progressBar.setProgress(event.getPercent()); - if (event.getPercent() == 100) { - stopButton.setVisibility(View.INVISIBLE); - scheduleDismiss(); - } - state = event.getStatus(); - }, FabricPrivacy::logException) - ); - } - - @Override - public void dismiss() { - if (L.isEnabled(L.UI)) - log.debug("dismiss"); - try { - super.dismiss(); - } catch (IllegalStateException e) { - // dialog not running yet. onResume will try again. Set bolusEnded to make extra - // sure onResume will catch this - bolusEnded = true; - log.error("Unhandled exception", e); - } - if (helperActivity != null) { - helperActivity.finish(); - } - } - - @Override - public void onPause() { - if (L.isEnabled(L.UI)) - log.debug("onPause"); - running = false; - super.onPause(); - disposable.clear(); - } - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - outState.putString("state", state); - log.debug("storing state: " + state); - super.onSaveInstanceState(outState); - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.overview_bolusprogress_stop: - if (L.isEnabled(L.UI)) - log.debug("Stop bolus delivery button pressed"); - stopPressed = true; - stopPressedView.setVisibility(View.VISIBLE); - stopButton.setVisibility(View.INVISIBLE); - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelAllBoluses(); - break; - } - } - - private void scheduleDismiss() { - if (L.isEnabled(L.UI)) - log.debug("scheduleDismiss"); - Thread t = new Thread(() -> { - SystemClock.sleep(5000); - BolusProgressDialog.bolusEnded = true; - Activity activity = getActivity(); - if (activity != null) { - activity.runOnUiThread(() -> { - try { - if (running) { - if (L.isEnabled(L.UI)) - log.debug("executing"); - dismiss(); - } - } catch (Exception e) { - log.error("Unhandled exception", e); - } - }); - } - }); - t.start(); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/BolusProgressHelperActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/BolusProgressHelperActivity.java deleted file mode 100644 index 54f6aa7688..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/BolusProgressHelperActivity.java +++ /dev/null @@ -1,20 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs; - -import android.os.Bundle; - -import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; - -public class BolusProgressHelperActivity extends NoSplashAppCompatActivity { - public BolusProgressHelperActivity() { - super(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - BolusProgressDialog bolusProgressDialog = new BolusProgressDialog(); - bolusProgressDialog.setHelperActivity(this); - bolusProgressDialog.setInsulin(getIntent().getDoubleExtra("insulin", 0d)); - bolusProgressDialog.show(getSupportFragmentManager(), "BolusProgress"); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/CalibrationDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/CalibrationDialog.java deleted file mode 100644 index a82f94d2ff..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/CalibrationDialog.java +++ /dev/null @@ -1,94 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs; - - -import android.content.Context; -import android.os.Bundle; -import androidx.fragment.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.TextView; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.DecimalFormat; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.utils.NumberPicker; -import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.utils.XdripCalibrations; - -public class CalibrationDialog extends DialogFragment implements View.OnClickListener { - private static Logger log = LoggerFactory.getLogger(CalibrationDialog.class); - - NumberPicker bgNumber; - TextView unitsView; - - Context context; - - public CalibrationDialog() { - // Required empty public constructor - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - this.context = context; - } - - @Override - public void onDetach() { - super.onDetach(); - this.context = null; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.overview_calibration_dialog, container, false); - - getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); - getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - - view.findViewById(R.id.ok).setOnClickListener(this); - view.findViewById(R.id.cancel).setOnClickListener(this); - - String units = ProfileFunctions.getInstance().getProfileUnits(); - Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, units); - - bgNumber = (NumberPicker) view.findViewById(R.id.overview_calibration_bg); - - if (units.equals(Constants.MMOL)) - bgNumber.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok)); - else - bgNumber.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok)); - - unitsView = (TextView) view.findViewById(R.id.overview_calibration_units); - unitsView.setText(units); - - setCancelable(true); - getDialog().setCanceledOnTouchOutside(false); - return view; - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - final Double bg = SafeParse.stringToDouble(bgNumber.getText()); - XdripCalibrations.confirmAndSendCalibration(bg, context); - dismiss(); - break; - case R.id.cancel: - dismiss(); - break; - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewCarbsDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewCarbsDialog.java deleted file mode 100644 index 23352f3d5b..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewCarbsDialog.java +++ /dev/null @@ -1,444 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs; - -import android.os.Bundle; -import android.os.HandlerThread; -import androidx.fragment.app.DialogFragment; -import androidx.appcompat.app.AlertDialog; -import android.text.Editable; -import android.text.Html; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.RadioButton; - -import com.google.common.base.Joiner; - -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.DecimalFormat; -import java.util.LinkedList; -import java.util.List; - -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.db.BgReading; -import info.nightscout.androidaps.db.CareportalEvent; -import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.plugins.treatments.CarbsGenerator; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.DefaultValueHelper; -import info.nightscout.androidaps.utils.NumberPicker; -import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.ToastUtils; - -import static info.nightscout.androidaps.utils.DateUtil.now; - -public class NewCarbsDialog extends DialogFragment implements OnClickListener, CompoundButton.OnCheckedChangeListener { - private static Logger log = LoggerFactory.getLogger(NewCarbsDialog.class); - - private static final int FAV1_DEFAULT = 5; - private static final int FAV2_DEFAULT = 10; - private static final int FAV3_DEFAULT = 20; - - private RadioButton startActivityTTCheckbox; - private RadioButton startEatingSoonTTCheckbox; - private RadioButton startHypoTTCheckbox; - private boolean togglingTT; - - private NumberPicker editTime; - private NumberPicker editDuration; - private NumberPicker editCarbs; - private Integer maxCarbs; - - private EditText notesEdit; - - //one shot guards - private boolean accepted; - private boolean okClicked; - - public NewCarbsDialog() { - } - - final private TextWatcher textWatcher = new TextWatcher() { - @Override - public void afterTextChanged(Editable s) { - validateInputs(); - } - - @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 void validateInputs() { - int time = editTime.getValue().intValue(); - if (time > 12 * 60 || time < -12 * 60) { - editTime.setValue(0d); - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.constraintapllied)); - } - Double duration = editDuration.getValue(); - if (duration > 10) { - editDuration.setValue(0d); - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.constraintapllied)); - } - int carbs = editCarbs.getValue().intValue(); - if (carbs > maxCarbs) { - editCarbs.setValue(0d); - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.carbsconstraintapplied)); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.overview_newcarbs_dialog, container, false); - - view.findViewById(R.id.ok).setOnClickListener(this); - view.findViewById(R.id.cancel).setOnClickListener(this); - - getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); - getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - - startActivityTTCheckbox = view.findViewById(R.id.newcarbs_activity_tt); - startActivityTTCheckbox.setOnCheckedChangeListener(this); - startEatingSoonTTCheckbox = view.findViewById(R.id.newcarbs_eating_soon_tt); - startEatingSoonTTCheckbox.setOnCheckedChangeListener(this); - startHypoTTCheckbox = view.findViewById(R.id.newcarbs_hypo_tt); - - editTime = view.findViewById(R.id.newcarbs_time); - editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher); - - editDuration = view.findViewById(R.id.new_carbs_duration); - editDuration.setParams(0d, 0d, 10d, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher); - - maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value(); - - editCarbs = view.findViewById(R.id.newcarb_carbsamount); - editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher); - - Button fav1Button = view.findViewById(R.id.newcarbs_plus1); - fav1Button.setOnClickListener(this); - fav1Button.setText(toSignedString(SP.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT))); - - Button fav2Button = view.findViewById(R.id.newcarbs_plus2); - fav2Button.setOnClickListener(this); - fav2Button.setText(toSignedString(SP.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT))); - - Button fav3Button = view.findViewById(R.id.newcarbs_plus3); - fav3Button.setOnClickListener(this); - fav3Button.setText(toSignedString(SP.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT))); - - LinearLayout notesLayout = view.findViewById(R.id.newcarbs_notes_layout); - notesLayout.setVisibility(SP.getBoolean(R.string.key_show_notes_entry_dialogs, false) ? View.VISIBLE : View.GONE); - notesEdit = view.findViewById(R.id.newcarbs_notes); - - BgReading bgReading = DatabaseHelper.actualBg(); - if (bgReading != null && bgReading.value < 72) { - startHypoTTCheckbox.setChecked(true); - // see #onCheckedChanged why listeners are registered like this - startHypoTTCheckbox.setOnClickListener(this); - } else { - startHypoTTCheckbox.setOnCheckedChangeListener(this); - } - - setCancelable(true); - getDialog().setCanceledOnTouchOutside(false); - - //recovering state if there is something - if (savedInstanceState != null) { - editCarbs.setValue(savedInstanceState.getDouble("editCarbs")); - editTime.setValue(savedInstanceState.getDouble("editTime")); - editDuration.setValue(savedInstanceState.getDouble("editDuration")); - } - return view; - } - - private String toSignedString(int value) { - return value > 0 ? "+" + value : String.valueOf(value); - } - - - @Override - public void onSaveInstanceState(Bundle carbsDialogState) { - carbsDialogState.putBoolean("startActivityTTCheckbox",startActivityTTCheckbox.isChecked()); - carbsDialogState.putBoolean("startEatingSoonTTCheckbox", startEatingSoonTTCheckbox.isChecked()); - carbsDialogState.putBoolean("startHypoTTCheckbox", startHypoTTCheckbox.isChecked()); - carbsDialogState.putDouble("editTime", editTime.getValue()); - carbsDialogState.putDouble("editDuration", editDuration.getValue()); - carbsDialogState.putDouble("editCarbs", editCarbs.getValue()); - super.onSaveInstanceState(carbsDialogState); - } - - - @Override - public synchronized void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - submit(); - break; - case R.id.cancel: - dismiss(); - break; - case R.id.newcarbs_plus1: - editCarbs.setValue(Math.max(0, editCarbs.getValue() - + SP.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT))); - validateInputs(); - break; - case R.id.newcarbs_plus2: - editCarbs.setValue(Math.max(0, editCarbs.getValue() - + SP.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT))); - validateInputs(); - break; - case R.id.newcarbs_plus3: - editCarbs.setValue(Math.max(0, editCarbs.getValue() - + SP.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT))); - validateInputs(); - break; - case R.id.newcarbs_activity_tt: - if (togglingTT) { - togglingTT = false; - break; - } - startActivityTTCheckbox.setOnClickListener(null); - startActivityTTCheckbox.setOnCheckedChangeListener(null); - startActivityTTCheckbox.setChecked(false); - startActivityTTCheckbox.setOnCheckedChangeListener(this); - break; - case R.id.newcarbs_eating_soon_tt: - if (togglingTT) { - togglingTT = false; - break; - } - startEatingSoonTTCheckbox.setOnClickListener(null); - startEatingSoonTTCheckbox.setOnCheckedChangeListener(null); - startEatingSoonTTCheckbox.setChecked(false); - startEatingSoonTTCheckbox.setOnCheckedChangeListener(this); - break; - case R.id.newcarbs_hypo_tt: - if (togglingTT) { - togglingTT = false; - break; - } - startHypoTTCheckbox.setOnClickListener(null); - startHypoTTCheckbox.setOnCheckedChangeListener(null); - startHypoTTCheckbox.setChecked(false); - startHypoTTCheckbox.setOnCheckedChangeListener(this); - break; - } - } - - - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - // Logic to disable a selected radio when pressed: when a checked radio - // is pressed, no CheckChanged event is triggered, so register a Click event - // when checking a radio. Since Click events come after CheckChanged events, - // the Click event is triggered immediately after this. Thus, set togglingTT - // var to true, so that the first Click event fired after this is ignored. - // Radios remove themselves from Click events once unchecked. - // Since radios are not in a group, their state is manually updated here. - switch (buttonView.getId()) { - case R.id.newcarbs_activity_tt: - togglingTT = true; - startActivityTTCheckbox.setOnClickListener(this); - - startEatingSoonTTCheckbox.setOnCheckedChangeListener(null); - startEatingSoonTTCheckbox.setChecked(false); - startEatingSoonTTCheckbox.setOnCheckedChangeListener(this); - - startHypoTTCheckbox.setOnCheckedChangeListener(null); - startHypoTTCheckbox.setChecked(false); - startHypoTTCheckbox.setOnCheckedChangeListener(this); - break; - case R.id.newcarbs_eating_soon_tt: - togglingTT = true; - startEatingSoonTTCheckbox.setOnClickListener(this); - - startActivityTTCheckbox.setOnCheckedChangeListener(null); - startActivityTTCheckbox.setChecked(false); - startActivityTTCheckbox.setOnCheckedChangeListener(this); - - startHypoTTCheckbox.setOnCheckedChangeListener(null); - startHypoTTCheckbox.setChecked(false); - startHypoTTCheckbox.setOnCheckedChangeListener(this); - break; - case R.id.newcarbs_hypo_tt: - togglingTT = true; - startHypoTTCheckbox.setOnClickListener(this); - - startActivityTTCheckbox.setOnCheckedChangeListener(null); - startActivityTTCheckbox.setChecked(false); - startActivityTTCheckbox.setOnCheckedChangeListener(this); - - startEatingSoonTTCheckbox.setOnCheckedChangeListener(null); - startEatingSoonTTCheckbox.setChecked(false); - startEatingSoonTTCheckbox.setOnCheckedChangeListener(this); - break; - } - } - - private void submit() { - if (okClicked) { - log.debug("guarding: ok already clicked"); - dismiss(); - return; - } - okClicked = true; - try { - final Profile currentProfile = ProfileFunctions.getInstance().getProfile(); - if (currentProfile == null) { - return; - } - - int carbs = editCarbs.getValue().intValue(); - Integer carbsAfterConstraints = MainApp.getConstraintChecker().applyCarbsConstraints(new Constraint<>(carbs)).value(); - - final String units = currentProfile.getUnits(); - DefaultValueHelper helper = new DefaultValueHelper(); - - int activityTTDuration = helper.determineActivityTTDuration(); - double activityTT = helper.determineActivityTT(units); - - int eatingSoonTTDuration = helper.determineEatingSoonTTDuration(); - double eatingSoonTT = helper.determineEatingSoonTT(units); - - int hypoTTDuration = helper.determineHypoTTDuration(); - double hypoTT = helper.determineHypoTT(units); - - List actions = new LinkedList<>(); - - if (startActivityTTCheckbox.isChecked()) { - String unitLabel = "mg/dl"; - if (currentProfile.getUnits().equals(Constants.MMOL)) { - unitLabel = "mmol/l"; - } - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + activityTTDuration + " min)"); - } - if (startEatingSoonTTCheckbox.isChecked()) { - if (currentProfile.getUnits().equals(Constants.MMOL)) { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)"); - } else { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)"); - } - } - if (startHypoTTCheckbox.isChecked()) { - if (currentProfile.getUnits().equals(Constants.MMOL)) { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(hypoTT) + " mmol/l (" + hypoTTDuration + " min)"); - } else { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(hypoTT) + " mg/dl (" + hypoTTDuration + " min)"); - } - } - - int timeOffset = editTime.getValue().intValue(); - final long time = now() + timeOffset * 1000 * 60; - if (timeOffset != 0) { - actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(time)); - } - int duration = editDuration.getValue().intValue(); - if (duration > 0) { - actions.add(MainApp.gs(R.string.duration) + ": " + duration + MainApp.gs(R.string.shorthour)); - } - if (carbs > 0) { - actions.add(MainApp.gs(R.string.carbs) + ": " + "" + carbsAfterConstraints + "g" + ""); - } - if (!carbsAfterConstraints.equals(carbs)) { - actions.add("" + MainApp.gs(R.string.carbsconstraintapplied) + ""); - } - final String notes = notesEdit.getText().toString(); - if (!notes.isEmpty()) { - actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes); - } - - final double finalActivityTT = activityTT; - final int finalActivityTTDuration = activityTTDuration; - final double finalEatigSoonTT = eatingSoonTT; - final int finalEatingSoonTTDuration = eatingSoonTTDuration; - final double finalHypoTT = hypoTT; - final int finalHypoTTDuration = hypoTTDuration; - - final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - if (carbsAfterConstraints > 0 || startActivityTTCheckbox.isChecked() - || startEatingSoonTTCheckbox.isChecked() || startHypoTTCheckbox.isChecked()) { - builder.setMessage(Html.fromHtml(Joiner.on("
").join(actions))); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { - synchronized (builder) { - if (accepted) { - log.debug("guarding: already accepted"); - return; - } - accepted = true; - - if (startActivityTTCheckbox.isChecked()) { - TempTarget tempTarget = new TempTarget() - .date(System.currentTimeMillis()) - .duration(finalActivityTTDuration) - .reason(MainApp.gs(R.string.activity)) - .source(Source.USER) - .low(Profile.toMgdl(finalActivityTT, currentProfile.getUnits())) - .high(Profile.toMgdl(finalActivityTT, currentProfile.getUnits())); - TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); - } else if (startEatingSoonTTCheckbox.isChecked()) { - TempTarget tempTarget = new TempTarget() - .date(System.currentTimeMillis()) - .duration(finalEatingSoonTTDuration) - .reason(MainApp.gs(R.string.eatingsoon)) - .source(Source.USER) - .low(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits())) - .high(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits())); - TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); - } else if (startHypoTTCheckbox.isChecked()) { - TempTarget tempTarget = new TempTarget() - .date(System.currentTimeMillis()) - .duration(finalHypoTTDuration) - .reason(MainApp.gs(R.string.hypo)) - .source(Source.USER) - .low(Profile.toMgdl(finalHypoTT, currentProfile.getUnits())) - .high(Profile.toMgdl(finalHypoTT, currentProfile.getUnits())); - TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); - } - - if (carbsAfterConstraints > 0) { - if (duration == 0) { - CarbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes); - } else { - CarbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes); - NSUpload.uploadEvent(CareportalEvent.NOTE, now() - 2000, MainApp.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset)); - } - } - } - }); - } else { - builder.setMessage(MainApp.gs(R.string.no_action_selected)); - } - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - dismiss(); - } catch (Exception e) { - log.error("Unhandled exception", e); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewInsulinDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewInsulinDialog.java deleted file mode 100644 index d5d4308f63..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewInsulinDialog.java +++ /dev/null @@ -1,319 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.HandlerThread; -import androidx.fragment.app.DialogFragment; -import androidx.appcompat.app.AlertDialog; -import android.text.Editable; -import android.text.Html; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.LinearLayout; - -import com.google.common.base.Joiner; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.DecimalFormat; -import java.util.LinkedList; -import java.util.List; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.CareportalEvent; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.NumberPicker; -import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.utils.T; -import info.nightscout.androidaps.utils.ToastUtils; - -import static info.nightscout.androidaps.utils.DateUtil.now; - -public class NewInsulinDialog extends DialogFragment implements OnClickListener { - private static Logger log = LoggerFactory.getLogger(NewInsulinDialog.class); - - public static final double PLUS1_DEFAULT = 0.5d; - public static final double PLUS2_DEFAULT = 1d; - public static final double PLUS3_DEFAULT = 2d; - - private CheckBox startEatingSoonTTCheckbox; - private CheckBox recordOnlyCheckbox; - - private LinearLayout editLayout; - private NumberPicker editTime; - private NumberPicker editInsulin; - private Double maxInsulin; - - private EditText notesEdit; - - //one shot guards - private boolean accepted; - private boolean okClicked; - - public NewInsulinDialog() { - } - - final private TextWatcher textWatcher = new TextWatcher() { - @Override - public void afterTextChanged(Editable s) { - validateInputs(); - } - - @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 void validateInputs() { - int time = editTime.getValue().intValue(); - if (Math.abs(time) > 12 * 60) { - editTime.setValue(0d); - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.constraintapllied)); - } - Double insulin = editInsulin.getValue(); - if (insulin > maxInsulin) { - editInsulin.setValue(0d); - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.bolusconstraintapplied)); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.overview_newinsulin_dialog, container, false); - - view.findViewById(R.id.ok).setOnClickListener(this); - view.findViewById(R.id.cancel).setOnClickListener(this); - - getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); - getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - - startEatingSoonTTCheckbox = view.findViewById(R.id.newinsulin_start_eating_soon_tt); - - recordOnlyCheckbox = view.findViewById(R.id.newinsulin_record_only); - recordOnlyCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> editLayout.setVisibility(isChecked ? View.VISIBLE : View.GONE)); - - editLayout = view.findViewById(R.id.newinsulin_time_layout); - editLayout.setVisibility(View.GONE); - editTime = view.findViewById(R.id.newinsulin_time); - editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher); - - maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value(); - - editInsulin = view.findViewById(R.id.newinsulin_amount); - editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, view.findViewById(R.id.ok), textWatcher); - - Button plus1Button = view.findViewById(R.id.newinsulin_plus05); - plus1Button.setOnClickListener(this); - plus1Button.setText(toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT))); - Button plus2Button = view.findViewById(R.id.newinsulin_plus10); - plus2Button.setOnClickListener(this); - plus2Button.setText(toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT))); - Button plus3Button = view.findViewById(R.id.newinsulin_plus20); - plus3Button.setOnClickListener(this); - plus3Button.setText(toSignedString(SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT))); - - LinearLayout notesLayout = view.findViewById(R.id.newinsulin_notes_layout); - notesLayout.setVisibility(SP.getBoolean(R.string.key_show_notes_entry_dialogs, false) ? View.VISIBLE : View.GONE); - notesEdit = view.findViewById(R.id.newinsulin_notes); - - setCancelable(true); - getDialog().setCanceledOnTouchOutside(false); - if (savedInstanceState != null) { -// log.debug("savedInstanceState in onCreate is:" + savedInstanceState.toString()); - editInsulin.setValue(savedInstanceState.getDouble("editInsulin")); - editTime.setValue(savedInstanceState.getDouble("editTime")); - } - return view; - } - - private String toSignedString(double value) { - String formatted = DecimalFormatter.toPumpSupportedBolus(value); - return value > 0 ? "+" + formatted : formatted; - } - - @Override - public void onSaveInstanceState(Bundle insulinDialogState) { - insulinDialogState.putBoolean("startEatingSoonTTCheckbox", startEatingSoonTTCheckbox.isChecked()); - insulinDialogState.putBoolean("recordOnlyCheckbox", recordOnlyCheckbox.isChecked()); - insulinDialogState.putDouble("editTime", editTime.getValue()); - insulinDialogState.putDouble("editInsulin", editInsulin.getValue()); - insulinDialogState.putString("notesEdit", notesEdit.getText().toString()); - log.debug("Instance state saved:" + insulinDialogState.toString()); - super.onSaveInstanceState(insulinDialogState); - } - - @Override - public synchronized void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - submit(); - break; - case R.id.cancel: - dismiss(); - break; - case R.id.newinsulin_plus05: - editInsulin.setValue(Math.max(0, editInsulin.getValue() - + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT))); - validateInputs(); - break; - case R.id.newinsulin_plus10: - editInsulin.setValue(Math.max(0, editInsulin.getValue() - + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT))); - validateInputs(); - break; - case R.id.newinsulin_plus20: - editInsulin.setValue(Math.max(0, editInsulin.getValue() - + SP.getDouble(MainApp.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT))); - validateInputs(); - break; - } - } - - private void submit() { - if (okClicked) { - log.debug("guarding: ok already clicked"); - dismiss(); - return; - } - okClicked = true; - - try { - Profile currentProfile = ProfileFunctions.getInstance().getProfile(); - final PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - if (currentProfile == null || pump == null) - return; - - Double insulin = SafeParse.stringToDouble(editInsulin.getText()); - Double insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(insulin)).value(); - - List actions = new LinkedList<>(); - if (insulin > 0) { - actions.add(MainApp.gs(R.string.bolus) + ": " + "" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + ""); - if (recordOnlyCheckbox.isChecked()) { - actions.add("" + MainApp.gs(R.string.bolusrecordedonly) + ""); - } - } - - if (Math.abs(insulinAfterConstraints - insulin) > pump.getPumpDescription().pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) - actions.add(MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints)); - - int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration); - eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration; - double eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl); - eatingSoonTT = eatingSoonTT > 0 ? eatingSoonTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl; - - if (startEatingSoonTTCheckbox.isChecked()) { - if (currentProfile.getUnits().equals(Constants.MMOL)) { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)"); - } else - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)"); - } - - int timeOffset = editTime.getValue().intValue(); - final long time = now() + T.mins(timeOffset).msecs(); - if (timeOffset != 0) { - actions.add(MainApp.gs(R.string.time) + ": " + DateUtil.dateAndTimeString(time)); - } - final String notes = notesEdit.getText().toString(); - if (!notes.isEmpty()) { - actions.add(MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes); - } - - final double finalInsulinAfterConstraints = insulinAfterConstraints; - final double finalEatigSoonTT = eatingSoonTT; - final int finalEatingSoonTTDuration = eatingSoonTTDuration; - - final Context context = getContext(); - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - - builder.setTitle(MainApp.gs(R.string.confirmation)); - if (finalInsulinAfterConstraints > 0 || startEatingSoonTTCheckbox.isChecked()) { - builder.setMessage(Html.fromHtml(Joiner.on("
").join(actions))); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { - synchronized (builder) { - if (accepted) { - log.debug("guarding: already accepted"); - return; - } - accepted = true; - - if (startEatingSoonTTCheckbox.isChecked()) { - TempTarget tempTarget = new TempTarget() - .date(System.currentTimeMillis()) - .duration(finalEatingSoonTTDuration) - .reason(MainApp.gs(R.string.eatingsoon)) - .source(Source.USER) - .low(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits())) - .high(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits())); - TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); - } - - if (finalInsulinAfterConstraints > 0) { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; - detailedBolusInfo.insulin = finalInsulinAfterConstraints; - detailedBolusInfo.context = context; - detailedBolusInfo.source = Source.USER; - detailedBolusInfo.notes = notes; - if (recordOnlyCheckbox.isChecked()) { - detailedBolusInfo.date = time; - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); - } else { - detailedBolusInfo.date = now(); - ConfigBuilderPlugin.getPlugin().getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } - } - }); - } - } - } - }); - } else { - builder.setMessage(MainApp.gs(R.string.no_action_selected)); - } - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - dismiss(); - } catch (Exception e) { - log.error("Unhandled exception", e); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewTreatmentDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewTreatmentDialog.java deleted file mode 100644 index 9f2f42b2d9..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/NewTreatmentDialog.java +++ /dev/null @@ -1,213 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview.dialogs; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import androidx.fragment.app.DialogFragment; -import androidx.appcompat.app.AlertDialog; -import android.text.Editable; -import android.text.Html; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.CheckBox; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.DecimalFormat; -import java.util.Objects; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.db.CareportalEvent; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.NumberPicker; -import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.utils.ToastUtils; - -public class NewTreatmentDialog extends DialogFragment implements OnClickListener { - private static Logger log = LoggerFactory.getLogger(NewTreatmentDialog.class); - - private NumberPicker editCarbs; - private NumberPicker editInsulin; - - private Integer maxCarbs; - private Double maxInsulin; - - //one shot guards - private boolean accepted; - private boolean okClicked; - - private CheckBox recordOnlyCheckbox; - - public NewTreatmentDialog() { - } - - final private 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) { - validateInputs(); - } - }; - - private void validateInputs() { - Integer carbs = SafeParse.stringToInt(editCarbs.getText()); - if (carbs > maxCarbs) { - editCarbs.setValue(0d); - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.carbsconstraintapplied)); - } - Double insulin = SafeParse.stringToDouble(editInsulin.getText()); - if (insulin > maxInsulin) { - editInsulin.setValue(0d); - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.bolusconstraintapplied)); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.overview_newtreatment_dialog, container, false); - - view.findViewById(R.id.ok).setOnClickListener(this); - view.findViewById(R.id.cancel).setOnClickListener(this); - - getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); - getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - - maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value(); - maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value(); - - editCarbs = (NumberPicker) view.findViewById(R.id.treatments_newtreatment_carbsamount); - editInsulin = (NumberPicker) view.findViewById(R.id.treatments_newtreatment_insulinamount); - - editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher); - editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, view.findViewById(R.id.ok), textWatcher); - - recordOnlyCheckbox = (CheckBox) view.findViewById(R.id.newtreatment_record_only); - - setCancelable(true); - getDialog().setCanceledOnTouchOutside(false); - return view; - } - - @Override - public synchronized void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - if (okClicked) { - log.debug("guarding: ok already clicked"); - dismiss(); - return; - } - okClicked = true; - - try { - final PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - - if (pump == null) - return; - - Double insulin = SafeParse.stringToDouble(editInsulin.getText()); - final Integer carbs = SafeParse.stringToInt(editCarbs.getText()); - - String confirmMessage = MainApp.gs(R.string.entertreatmentquestion) + "
"; - - Double insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(insulin)).value(); - Integer carbsAfterConstraints = MainApp.getConstraintChecker().applyCarbsConstraints(new Constraint<>(carbs)).value(); - - if (insulin > 0) { - confirmMessage += MainApp.gs(R.string.bolus) + ": " + "" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + ""; - if (recordOnlyCheckbox.isChecked()) { - confirmMessage += "
" + MainApp.gs(R.string.bolusrecordedonly) + ""; - } - if (Math.abs(insulinAfterConstraints - insulin) > pump.getPumpDescription().pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) || !Objects.equals(carbsAfterConstraints, carbs)) - confirmMessage += "
" + MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints); - } - if (carbsAfterConstraints > 0) - confirmMessage += "
" + MainApp.gs(R.string.carbs) + ": " + "" + carbsAfterConstraints + "g" + ""; - - - final double finalInsulinAfterConstraints = insulinAfterConstraints; - final int finalCarbsAfterConstraints = carbsAfterConstraints; - - final Context context = getContext(); - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(Html.fromHtml(confirmMessage)); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - synchronized (builder) { - if (accepted) { - log.debug("guarding: already accepted"); - return; - } - accepted = true; - if (finalInsulinAfterConstraints > 0 || finalCarbsAfterConstraints > 0) { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - if (finalInsulinAfterConstraints == 0) - detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION; - if (finalCarbsAfterConstraints == 0) - detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; - detailedBolusInfo.insulin = finalInsulinAfterConstraints; - detailedBolusInfo.carbs = finalCarbsAfterConstraints; - detailedBolusInfo.context = context; - detailedBolusInfo.source = Source.USER; - if (!(recordOnlyCheckbox.isChecked() && (detailedBolusInfo.insulin > 0 || ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().storesCarbInfo))) { - ConfigBuilderPlugin.getPlugin().getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } - } - }); - } else { - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); - } - } - } - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - - dismiss(); - } catch (Exception e) { - log.error("Unhandled exception", e); - } - break; - case R.id.cancel: - dismiss(); - break; - } - - } - -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java index 6de3e2c335..dec943365c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java @@ -69,7 +69,7 @@ public class GraphData { private IobCobCalculatorPlugin iobCobCalculatorPlugin; public GraphData(GraphView graph, IobCobCalculatorPlugin iobCobCalculatorPlugin) { - units = ProfileFunctions.getInstance().getProfileUnits(); + units = ProfileFunctions.getSystemUnits(); this.graph = graph; this.iobCobCalculatorPlugin = iobCobCalculatorPlugin; } @@ -264,9 +264,9 @@ public class GraphData { TempTarget tt = TreatmentsPlugin.getPlugin().getTempTargetFromHistory(time); double value; if (tt == null) { - value = (profile.getTargetLow(time) + profile.getTargetHigh(time)) / 2; + value = Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, ProfileFunctions.getSystemUnits()); } else { - value = Profile.fromMgdlToUnits(tt.target(), profile.getUnits()); + value = Profile.fromMgdlToUnits(tt.target(), ProfileFunctions.getSystemUnits()); } if (lastTarget != value) { if (lastTarget != -1) @@ -371,7 +371,8 @@ public class GraphData { actArrayHist.add(new ScaledDataPoint(time, act, actScale)); else actArrayPred.add(new ScaledDataPoint(time, act, actScale)); - if (act > maxIAValue) maxIAValue = act; + + maxIAValue = Math.max(maxIAValue, Math.abs(act)); } ScaledDataPoint[] actData = new ScaledDataPoint[actArrayHist.size()]; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java index 28b0abd33d..dde005ba20 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java @@ -78,6 +78,7 @@ public class Notification { public static final int USERMESSAGE = 53; public static final int OVER_24H_TIME_CHANGE_REQUESTED = 54; public static final int INVALID_VERSION = 55; + public static final int PERMISSION_SYSTEM_WINDOW = 56; public int id; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationRecyclerViewAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationRecyclerViewAdapter.java index 1e96b02be1..8d1664c7e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationRecyclerViewAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationRecyclerViewAdapter.java @@ -21,7 +21,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.general.nsclient.broadcasts.BroadcastAckAlarm; +import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.utils.DateUtil; @@ -93,7 +93,7 @@ public class NotificationRecyclerViewAdapter extends RecyclerView.Adapter updateGui(), FabricPrivacy::logException) - ); - updateGui(); - } - - @Override - public synchronized void onPause() { - super.onPause(); - disposable.clear(); - } - - protected void updateGui() { - class CustomComparator implements Comparator { - public int compare(Sms object1, Sms object2) { - return (int) (object1.date - object2.date); - } - } - Collections.sort(SmsCommunicatorPlugin.getPlugin().messages, new CustomComparator()); - int messagesToShow = 40; - - int start = Math.max(0, SmsCommunicatorPlugin.getPlugin().messages.size() - messagesToShow); - - String logText = ""; - for (int x = start; x < SmsCommunicatorPlugin.getPlugin().messages.size(); x++) { - Sms sms = SmsCommunicatorPlugin.getPlugin().messages.get(x); - if (sms.ignored) { - logText += DateUtil.timeString(sms.date) + " <<< " + "░ " + sms.phoneNumber + " " + sms.text + "
"; - } else if (sms.received) { - logText += DateUtil.timeString(sms.date) + " <<< " + (sms.processed ? "● " : "○ ") + sms.phoneNumber + " " + sms.text + "
"; - } else if (sms.sent) { - logText += DateUtil.timeString(sms.date) + " >>> " + (sms.processed ? "● " : "○ ") + sms.phoneNumber + " " + sms.text + "
"; - } - } - logView.setText(Html.fromHtml(logText)); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt new file mode 100644 index 0000000000..90ec078931 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt @@ -0,0 +1,70 @@ +package info.nightscout.androidaps.plugins.general.smsCommunicator + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.R +import info.nightscout.androidaps.plugins.bus.RxBus.toObservable +import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.HtmlHelper +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.smscommunicator_fragment.* +import java.util.* +import kotlin.math.max + +class SmsCommunicatorFragment : Fragment() { + private val disposable = CompositeDisposable() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.smscommunicator_fragment, container, false) + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable.add(toObservable(EventSmsCommunicatorUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGui() }) { FabricPrivacy.logException(it) } + ) + updateGui() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + fun updateGui() { + class CustomComparator : Comparator { + override fun compare(object1: Sms, object2: Sms): Int { + return (object1.date - object2.date).toInt() + } + } + Collections.sort(SmsCommunicatorPlugin.messages, CustomComparator()) + val messagesToShow = 40 + val start = max(0, SmsCommunicatorPlugin.messages.size - messagesToShow) + var logText = "" + for (x in start until SmsCommunicatorPlugin.messages.size) { + val sms = SmsCommunicatorPlugin.messages[x] + when { + sms.ignored -> { + logText += DateUtil.timeString(sms.date) + " <<< " + "░ " + sms.phoneNumber + " " + sms.text + "
" + } + sms.received -> { + logText += DateUtil.timeString(sms.date) + " <<< " + (if (sms.processed) "● " else "○ ") + sms.phoneNumber + " " + sms.text + "
" + } + sms.sent -> { + logText += DateUtil.timeString(sms.date) + " >>> " + (if (sms.processed) "● " else "○ ") + sms.phoneNumber + " " + sms.text + "
" + } + } + } + smscommunicator_log?.text = HtmlHelper.fromHtml(logText) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java deleted file mode 100644 index 264d7c3e52..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java +++ /dev/null @@ -1,812 +0,0 @@ -package info.nightscout.androidaps.plugins.general.smsCommunicator; - -import android.content.Intent; -import android.os.Bundle; -import android.telephony.SmsManager; -import android.telephony.SmsMessage; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.Normalizer; -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.data.ProfileStore; -import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.events.EventRefreshOverview; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; -import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.utils.XdripCalibrations; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -/** - * Created by mike on 05.08.2016. - */ -public class SmsCommunicatorPlugin extends PluginBase { - private static Logger log = LoggerFactory.getLogger(L.SMS); - private CompositeDisposable disposable = new CompositeDisposable(); - - private static SmsCommunicatorPlugin smsCommunicatorPlugin; - - public static SmsCommunicatorPlugin getPlugin() { - - if (smsCommunicatorPlugin == null) { - smsCommunicatorPlugin = new SmsCommunicatorPlugin(); - } - return smsCommunicatorPlugin; - } - - List allowedNumbers = new ArrayList<>(); - - AuthRequest messageToConfirm = null; - - long lastRemoteBolusTime = 0; - - ArrayList messages = new ArrayList<>(); - - SmsCommunicatorPlugin() { - super(new PluginDescription() - .mainType(PluginType.GENERAL) - .fragmentClass(SmsCommunicatorFragment.class.getName()) - .pluginName(R.string.smscommunicator) - .shortName(R.string.smscommunicator_shortname) - .preferencesId(R.xml.pref_smscommunicator) - .description(R.string.description_sms_communicator) - ); - processSettings(null); - } - - @Override - protected void onStart() { - super.onStart(); - disposable.add(RxBus.INSTANCE - .toObservable(EventPreferenceChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - processSettings(event); - }, FabricPrivacy::logException) - ); - } - - @Override - protected void onStop() { - disposable.clear(); - super.onStop(); - } - - private void processSettings(final EventPreferenceChange ev) { - if (ev == null || ev.isChanged(R.string.key_smscommunicator_allowednumbers)) { - String settings = SP.getString(R.string.key_smscommunicator_allowednumbers, ""); - - allowedNumbers.clear(); - String[] substrings = settings.split(";"); - for (String number : substrings) { - String cleaned = number.replaceAll("\\s+", ""); - allowedNumbers.add(cleaned); - log.debug("Found allowed number: " + cleaned); - } - } - } - - boolean isCommand(String command, String number) { - switch (command.toUpperCase()) { - case "BG": - case "LOOP": - case "TREATMENTS": - case "NSCLIENT": - case "PUMP": - case "BASAL": - case "BOLUS": - case "EXTENDED": - case "CAL": - case "PROFILE": - return true; - } - if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(number)) - return true; - return false; - } - - boolean isAllowedNumber(String number) { - for (String num : allowedNumbers) { - if (num.equals(number)) return true; - } - return false; - } - - public void handleNewData(Intent intent) { - Bundle bundle = intent.getExtras(); - if (bundle == null) return; - - Object[] pdus = (Object[]) bundle.get("pdus"); - if (pdus != null) { - // For every SMS message received - for (Object pdu : pdus) { - SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu); - processSms(new Sms(message)); - } - } - } - - void processSms(final Sms receivedSms) { - if (!isEnabled(PluginType.GENERAL)) { - log.debug("Ignoring SMS. Plugin disabled."); - return; - } - if (!isAllowedNumber(receivedSms.phoneNumber)) { - log.debug("Ignoring SMS from: " + receivedSms.phoneNumber + ". Sender not allowed"); - receivedSms.ignored = true; - messages.add(receivedSms); - RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui()); - return; - } - - messages.add(receivedSms); - log.debug(receivedSms.toString()); - - String[] splitted = receivedSms.text.split("\\s+"); - boolean remoteCommandsAllowed = SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false); - - if (splitted.length > 0 && isCommand(splitted[0].toUpperCase(), receivedSms.phoneNumber)) { - switch (splitted[0].toUpperCase()) { - case "BG": - processBG(splitted, receivedSms); - break; - case "LOOP": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 || splitted.length == 3) - processLOOP(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "TREATMENTS": - if (splitted.length == 2) - processTREATMENTS(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "NSCLIENT": - if (splitted.length == 2) - processNSCLIENT(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "PUMP": - processPUMP(splitted, receivedSms); - break; - case "PROFILE": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 || splitted.length == 3) - processPROFILE(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "BASAL": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 || splitted.length == 3) - processBASAL(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "EXTENDED": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 || splitted.length == 3) - processEXTENDED(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "BOLUS": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2 && DateUtil.now() - lastRemoteBolusTime < Constants.remoteBolusMinDistance) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed)); - else if (splitted.length == 2 && ConfigBuilderPlugin.getPlugin().getActivePump().isSuspended()) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.pumpsuspended)); - else if (splitted.length == 2) - processBOLUS(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - case "CAL": - if (!remoteCommandsAllowed) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)); - else if (splitted.length == 2) - processCAL(splitted, receivedSms); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - default: // expect passCode here - if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(receivedSms.phoneNumber)) { - messageToConfirm.action(splitted[0]); - messageToConfirm = null; - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)); - break; - } - } - - RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui()); - } - - @SuppressWarnings("unused") - private void processBG(String[] splitted, Sms receivedSms) { - BgReading actualBG = DatabaseHelper.actualBg(); - BgReading lastBG = DatabaseHelper.lastBg(); - - String reply = ""; - - String units = ProfileFunctions.getInstance().getProfileUnits(); - - if (actualBG != null) { - reply = MainApp.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", "; - } else if (lastBG != null) { - Long agoMsec = System.currentTimeMillis() - lastBG.date; - int agoMin = (int) (agoMsec / 60d / 1000d); - reply = MainApp.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsToString(units) + " " + String.format(MainApp.gs(R.string.sms_minago), agoMin) + ", "; - } - GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); - if (glucoseStatus != null) - reply += MainApp.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", "; - - TreatmentsPlugin.getPlugin().updateTotalIOBTreatments(); - IobTotal bolusIob = TreatmentsPlugin.getPlugin().getLastCalculationTreatments().round(); - TreatmentsPlugin.getPlugin().updateTotalIOBTempBasals(); - IobTotal basalIob = TreatmentsPlugin.getPlugin().getLastCalculationTempBasals().round(); - - String cobText = MainApp.gs(R.string.value_unavailable_short); - CobInfo cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "SMS COB"); - - reply += MainApp.gs(R.string.sms_iob) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" - + MainApp.gs(R.string.sms_bolus) + " " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U " - + MainApp.gs(R.string.sms_basal) + " " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U), " - + MainApp.gs(R.string.cob) + ": " + cobInfo.generateCOBString(); - - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - receivedSms.processed = true; - } - - private void processLOOP(String[] splitted, Sms receivedSms) { - String reply; - switch (splitted[1].toUpperCase()) { - case "DISABLE": - case "STOP": - LoopPlugin loopPlugin = LoopPlugin.getPlugin(); - if (loopPlugin.isEnabled(PluginType.LOOP)) { - loopPlugin.setPluginEnabled(PluginType.LOOP, false); - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() { - @Override - public void run() { - RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_STOP")); - String reply = MainApp.gs(R.string.smscommunicator_loophasbeendisabled) + " " + - MainApp.gs(result.success ? R.string.smscommunicator_tempbasalcanceled : R.string.smscommunicator_tempbasalcancelfailed); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - }); - } else { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisdisabled)); - } - receivedSms.processed = true; - break; - case "ENABLE": - case "START": - loopPlugin = LoopPlugin.getPlugin(); - if (!loopPlugin.isEnabled(PluginType.LOOP)) { - loopPlugin.setPluginEnabled(PluginType.LOOP, true); - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loophasbeenenabled)); - RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_START")); - } else { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisenabled)); - } - receivedSms.processed = true; - break; - case "STATUS": - loopPlugin = LoopPlugin.getPlugin(); - if (loopPlugin.isEnabled(PluginType.LOOP)) { - if (loopPlugin.isSuspended()) - reply = String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()); - else - reply = MainApp.gs(R.string.smscommunicator_loopisenabled); - } else { - reply = MainApp.gs(R.string.smscommunicator_loopisdisabled); - } - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - receivedSms.processed = true; - break; - case "RESUME": - LoopPlugin.getPlugin().suspendTo(0); - RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_RESUME")); - NSUpload.uploadOpenAPSOffline(0); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopresumed)); - break; - case "SUSPEND": - int duration = 0; - if (splitted.length == 3) - duration = SafeParse.stringToInt(splitted[2]); - duration = Math.max(0, duration); - duration = Math.min(180, duration); - if (duration == 0) { - receivedSms.processed = true; - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_wrongduration)); - return; - } else { - String passCode = generatePasscode(); - reply = String.format(MainApp.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(duration) { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() { - @Override - public void run() { - if (result.success) { - LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger * 60L * 1000); - NSUpload.uploadOpenAPSOffline(anInteger * 60); - RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_SUSPENDED")); - String reply = MainApp.gs(R.string.smscommunicator_loopsuspended) + " " + - MainApp.gs(result.success ? R.string.smscommunicator_tempbasalcanceled : R.string.smscommunicator_tempbasalcancelfailed); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - - } - }); - } - break; - default: - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - break; - } - } - - private void processTREATMENTS(String[] splitted, Sms receivedSms) { - if (splitted[1].toUpperCase().equals("REFRESH")) { - TreatmentsPlugin.getPlugin().getService().resetTreatments(); - RxBus.INSTANCE.send(new EventNSClientRestart()); - sendSMS(new Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT")); - receivedSms.processed = true; - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - private void processNSCLIENT(String[] splitted, Sms receivedSms) { - if (splitted[1].toUpperCase().equals("RESTART")) { - RxBus.INSTANCE.send(new EventNSClientRestart()); - sendSMS(new Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT")); - receivedSms.processed = true; - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - @SuppressWarnings("unused") - private void processPUMP(String[] splitted, Sms receivedSms) { - ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("SMS", new Callback() { - @Override - public void run() { - PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - if (result.success) { - if (pump != null) { - String reply = pump.shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } else { - String reply = MainApp.gs(R.string.readstatusfailed); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - receivedSms.processed = true; - } - - private void processPROFILE(String[] splitted, Sms receivedSms) { - // load profiles - ProfileInterface anInterface = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface(); - if (anInterface == null) { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.notconfigured)); - receivedSms.processed = true; - return; - } - ProfileStore store = anInterface.getProfile(); - if (store == null) { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.notconfigured)); - receivedSms.processed = true; - return; - } - final ArrayList list = store.getProfileList(); - - if (splitted[1].toUpperCase().equals("STATUS")) { - sendSMS(new Sms(receivedSms.phoneNumber, ProfileFunctions.getInstance().getProfileName())); - } else if (splitted[1].toUpperCase().equals("LIST")) { - if (list.isEmpty()) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.invalidprofile)); - else { - String reply = ""; - for (int i = 0; i < list.size(); i++) { - if (i > 0) - reply += "\n"; - reply += (i + 1) + ". "; - reply += list.get(i); - } - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } else { - - int pindex = SafeParse.stringToInt(splitted[1]); - int percentage = 100; - if (splitted.length > 2) - percentage = SafeParse.stringToInt(splitted[2]); - - if (pindex > list.size()) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else if (percentage == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else if (pindex == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else { - final Profile profile = store.getSpecificProfile((String) list.get(pindex - 1)); - if (profile == null) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile)); - else { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_profilereplywithcode), list.get(pindex - 1), percentage, passCode); - receivedSms.processed = true; - int finalPercentage = percentage; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction((String) list.get(pindex - 1), finalPercentage) { - @Override - public void run() { - ProfileFunctions.doProfileSwitch(store, (String) list.get(pindex - 1), 0, finalPercentage, 0); - sendSMS(new Sms(receivedSms.phoneNumber, R.string.profileswitchcreated)); - } - }); - } - } - } - receivedSms.processed = true; - } - - private void processBASAL(String[] splitted, Sms receivedSms) { - if (splitted[1].toUpperCase().equals("CANCEL") || splitted[1].toUpperCase().equals("STOP")) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_basalstopreplywithcode), passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() { - @Override - public void run() { - if (result.success) { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalcanceled); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } else if (splitted[1].endsWith("%")) { - int tempBasalPct = SafeParse.stringToInt(StringUtils.removeEnd(splitted[1], "%")); - int duration = 30; - if (splitted.length > 2) - duration = SafeParse.stringToInt(splitted[2]); - final Profile profile = ProfileFunctions.getInstance().getProfile(); - - if (profile == null) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile)); - else if (tempBasalPct == 0 && !splitted[1].equals("0%")) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else if (duration == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else { - tempBasalPct = MainApp.getConstraintChecker().applyBasalPercentConstraints(new Constraint<>(tempBasalPct), profile).value(); - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(tempBasalPct, duration) { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalPercent(anInteger, secondInteger, true, profile, new Callback() { - @Override - public void run() { - if (result.success) { - String reply; - if (result.isPercent) - reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration); - else - reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } - } else { - Double tempBasal = SafeParse.stringToDouble(splitted[1]); - int duration = 30; - if (splitted.length > 2) - duration = SafeParse.stringToInt(splitted[2]); - final Profile profile = ProfileFunctions.getInstance().getProfile(); - if (profile == null) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile)); - else if (tempBasal == 0 && !splitted[1].equals("0")) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else if (duration == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else { - tempBasal = MainApp.getConstraintChecker().applyBasalConstraints(new Constraint<>(tempBasal), profile).value(); - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(tempBasal, duration) { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalAbsolute(aDouble, secondInteger, true, profile, new Callback() { - @Override - public void run() { - if (result.success) { - String reply; - if (result.isPercent) - reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration); - else - reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_tempbasalfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } - } - } - - private void processEXTENDED(String[] splitted, Sms receivedSms) { - if (splitted[1].toUpperCase().equals("CANCEL") || splitted[1].toUpperCase().equals("STOP")) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelExtended(new Callback() { - @Override - public void run() { - if (result.success) { - String reply = MainApp.gs(R.string.smscommunicator_extendedcanceled); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_extendedcancelfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } else if (splitted.length != 3) { - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } else { - Double extended = SafeParse.stringToDouble(splitted[1]); - int duration = SafeParse.stringToInt(splitted[2]); - extended = MainApp.getConstraintChecker().applyExtendedBolusConstraints(new Constraint<>(extended)).value(); - if (extended == 0 || duration == 0) - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - else { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(extended, duration) { - @Override - public void run() { - ConfigBuilderPlugin.getPlugin().getCommandQueue().extendedBolus(aDouble, secondInteger, new Callback() { - @Override - public void run() { - if (result.success) { - String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedset), aDouble, duration); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_extendedfailed); - reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } - } - } - - - private void processBOLUS(String[] splitted, Sms receivedSms) { - Double bolus = SafeParse.stringToDouble(splitted[1]); - bolus = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(bolus)).value(); - if (bolus > 0d) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(bolus) { - @Override - public void run() { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.insulin = aDouble; - detailedBolusInfo.source = Source.USER; - ConfigBuilderPlugin.getPlugin().getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - final boolean resultSuccess = result.success; - final double resultBolusDelivered = result.bolusDelivered; - ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("SMS", new Callback() { - @Override - public void run() { - PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - if (resultSuccess) { - String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered); - if (pump != null) - reply += "\n" + pump.shortStatus(true); - lastRemoteBolusTime = DateUtil.now(); - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply)); - } else { - String reply = MainApp.gs(R.string.smscommunicator_bolusfailed); - if (pump != null) - reply += "\n" + pump.shortStatus(true); - sendSMS(new Sms(receivedSms.phoneNumber, reply)); - } - } - }); - } - }); - } - }); - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - private void processCAL(String[] splitted, Sms receivedSms) { - Double cal = SafeParse.stringToDouble(splitted[1]); - if (cal > 0d) { - String passCode = generatePasscode(); - String reply = String.format(MainApp.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode); - receivedSms.processed = true; - messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(cal) { - @Override - public void run() { - boolean result = XdripCalibrations.sendIntent(aDouble); - if (result) - sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationsent)); - else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationfailed)); - } - }); - } else - sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat)); - } - - public boolean sendNotificationToAllNumbers(String text) { - boolean result = true; - for (int i = 0; i < allowedNumbers.size(); i++) { - Sms sms = new Sms(allowedNumbers.get(i), text); - result = result && sendSMS(sms); - } - return result; - } - - private void sendSMSToAllNumbers(Sms sms) { - for (String number : allowedNumbers) { - sms.phoneNumber = number; - sendSMS(sms); - } - } - - boolean sendSMS(Sms sms) { - SmsManager smsManager = SmsManager.getDefault(); - sms.text = stripAccents(sms.text); - - try { - if (L.isEnabled(L.SMS)) - log.debug("Sending SMS to " + sms.phoneNumber + ": " + sms.text); - if (sms.text.getBytes().length <= 140) - smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null); - else { - ArrayList parts = smsManager.divideMessage(sms.text); - smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts, - null, null); - } - - messages.add(sms); - } catch (IllegalArgumentException e) { - if (e.getMessage().equals("Invalid message body")) { - Notification notification = new Notification(Notification.INVALID_MESSAGE_BODY, MainApp.gs(R.string.smscommunicator_messagebody), Notification.NORMAL); - RxBus.INSTANCE.send(new EventNewNotification(notification)); - return false; - } else { - Notification notification = new Notification(Notification.INVALID_PHONE_NUMBER, MainApp.gs(R.string.smscommunicator_invalidphonennumber), Notification.NORMAL); - RxBus.INSTANCE.send(new EventNewNotification(notification)); - return false; - } - } catch (java.lang.SecurityException e) { - Notification notification = new Notification(Notification.MISSING_SMS_PERMISSION, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.NORMAL); - RxBus.INSTANCE.send(new EventNewNotification(notification)); - return false; - } - RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui()); - return true; - } - - private String generatePasscode() { - int startChar1 = 'A'; // on iphone 1st char is uppercase :) - String passCode = Character.toString((char) (startChar1 + Math.random() * ('z' - 'a' + 1))); - int startChar2 = Math.random() > 0.5 ? 'a' : 'A'; - passCode += Character.toString((char) (startChar2 + Math.random() * ('z' - 'a' + 1))); - int startChar3 = Math.random() > 0.5 ? 'a' : 'A'; - passCode += Character.toString((char) (startChar3 + Math.random() * ('z' - 'a' + 1))); - passCode = passCode.replace('l', 'k').replace('I', 'J'); - return passCode; - } - - private static String stripAccents(String s) { - s = Normalizer.normalize(s, Normalizer.Form.NFD); - s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", ""); - return s; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt new file mode 100644 index 0000000000..0021574812 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -0,0 +1,922 @@ +package info.nightscout.androidaps.plugins.general.smsCommunicator + +import android.content.Intent +import android.preference.EditTextPreference +import android.preference.Preference +import android.preference.Preference.OnPreferenceChangeListener +import android.preference.PreferenceFragment +import android.telephony.SmsManager +import android.telephony.SmsMessage +import android.text.TextUtils +import com.andreabaccega.widget.ValidatingEditTextPreference +import info.nightscout.androidaps.Config +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.DatabaseHelper +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.db.TempTarget +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.interfaces.Constraint +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.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.bus.RxBus.send +import info.nightscout.androidaps.plugins.bus.RxBus.toObservable +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.* +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import org.apache.commons.lang3.StringUtils +import org.slf4j.LoggerFactory +import java.text.Normalizer +import java.util.* + +object SmsCommunicatorPlugin : PluginBase(PluginDescription() + .mainType(PluginType.GENERAL) + .fragmentClass(SmsCommunicatorFragment::class.java.name) + .pluginName(R.string.smscommunicator) + .shortName(R.string.smscommunicator_shortname) + .preferencesId(R.xml.pref_smscommunicator) + .description(R.string.description_sms_communicator) +) { + private val log = LoggerFactory.getLogger(L.SMS) + private val disposable = CompositeDisposable() + var allowedNumbers: MutableList = ArrayList() + var messageToConfirm: AuthRequest? = null + var lastRemoteBolusTime: Long = 0 + var messages = ArrayList() + + val commands = mapOf( + "BG" to "BG", + "LOOP" to "LOOP STOP/DISABLE/START/ENABLE/RESUME/STATUS\nLOOP SUSPEND 20", + "TREATMENTS" to "TREATMENTS REFRESH", + "NSCLIENT" to "NSCLIENT RESTART", + "PUMP" to "PUMP", + "BASAL" to "BASAL STOP/CANCEL\nBASAL 0.3\nBASAL 0.3 20\nBASAL 30%\nBASAL 30% 20\n", + "BOLUS" to "BOLUS 1.2\nBOLUS 1.2 MEAL", + "EXTENDED" to "EXTENDED STOP/CANCEL\nEXTENDED 2 120", + "CAL" to "CAL 5.6", + "PROFILE" to "PROFILE STATUS/LIST\nPROFILE 1\nPROFILE 2 30", + "TARGET" to "TARGET MEAL/ACTIVITY/HYPO/STOP", + "SMS" to "SMS DISABLE/STOP", + "CARBS" to "CARBS 12\nCARBS 12 23:05\nCARBS 12 11:05PM", + "HELP" to "HELP\nHELP command" + ) + + init { + processSettings(null) + } + + override fun onStart() { + super.onStart() + disposable.add(toObservable(EventPreferenceChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventPreferenceChange? -> processSettings(event) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) } + ) + } + + override fun onStop() { + disposable.clear() + super.onStop() + } + + override fun preprocessPreferences(preferenceFragment: PreferenceFragment) { + super.preprocessPreferences(preferenceFragment) + val distance = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_remotebolusmindistance)) as ValidatingEditTextPreference? + ?: return + val allowedNumbers = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_allowednumbers)) as EditTextPreference? + ?: return + if (!areMoreNumbers(allowedNumbers.text)) { + distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)) + distance.isEnabled = false + } else { + distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + distance.isEnabled = true + } + allowedNumbers.onPreferenceChangeListener = OnPreferenceChangeListener { _: Preference?, newValue: Any -> + if (!areMoreNumbers(newValue as String)) { + distance.text = (Constants.remoteBolusMinDistance / (60 * 1000L)).toString() + distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + + ".\n" + + MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat)) + distance.isEnabled = false + } else { + distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance) + distance.isEnabled = true + } + true + } + } + + override fun updatePreferenceSummary(pref: Preference) { + super.updatePreferenceSummary(pref) + if (pref is EditTextPreference) { + val editTextPref = pref + if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.text == null || TextUtils.isEmpty(editTextPref.text.trim { it <= ' ' }))) { + pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary)) + } + } + } + + private fun processSettings(ev: EventPreferenceChange?) { + if (ev == null || ev.isChanged(R.string.key_smscommunicator_allowednumbers)) { + val settings = SP.getString(R.string.key_smscommunicator_allowednumbers, "") + allowedNumbers.clear() + val substrings = settings.split(";").toTypedArray() + for (number in substrings) { + val cleaned = number.replace("\\s+".toRegex(), "") + allowedNumbers.add(cleaned) + log.debug("Found allowed number: $cleaned") + } + } + } + + fun isCommand(command: String, number: String): Boolean { + var found = false + commands.forEach { (k, _) -> + if (k == command) found = true + } + return found || messageToConfirm?.requester?.phoneNumber == number + } + + fun isAllowedNumber(number: String): Boolean { + for (num in allowedNumbers) { + if (num == number) return true + } + return false + } + + fun handleNewData(intent: Intent) { + val bundle = intent.extras ?: return + val format = bundle.getString("format") ?: return + val pdus = bundle["pdus"] as Array<*> + for (pdu in pdus) { + val message = SmsMessage.createFromPdu(pdu as ByteArray, format) + processSms(Sms(message)) + } + } + + fun processSms(receivedSms: Sms) { + if (!isEnabled(PluginType.GENERAL)) { + log.debug("Ignoring SMS. Plugin disabled.") + return + } + if (!isAllowedNumber(receivedSms.phoneNumber)) { + log.debug("Ignoring SMS from: " + receivedSms.phoneNumber + ". Sender not allowed") + receivedSms.ignored = true + messages.add(receivedSms) + send(EventSmsCommunicatorUpdateGui()) + return + } + val pump = ConfigBuilderPlugin.getPlugin().activePump ?: return + messages.add(receivedSms) + log.debug(receivedSms.toString()) + val splitted = receivedSms.text.split(Regex("\\s+")).toTypedArray() + val remoteCommandsAllowed = SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false) + + val minDistance = + if (areMoreNumbers(SP.getString(R.string.key_smscommunicator_allowednumbers, ""))) + T.mins(SP.getLong(R.string.key_smscommunicator_remotebolusmindistance, T.msecs(Constants.remoteBolusMinDistance).mins())).msecs() + else Constants.remoteBolusMinDistance + + if (splitted.isNotEmpty() && isCommand(splitted[0].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) { + when (splitted[0].toUpperCase(Locale.getDefault())) { + "BG" -> + if (splitted.size == 1) processBG(receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "LOOP" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processLOOP(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "TREATMENTS" -> + if (splitted.size == 2) processTREATMENTS(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "NSCLIENT" -> + if (splitted.size == 2) processNSCLIENT(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "PUMP" -> + if (splitted.size == 1) processPUMP(receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "PROFILE" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processPROFILE(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "BASAL" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processBASAL(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "EXTENDED" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processEXTENDED(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "BOLUS" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 && DateUtil.now() - lastRemoteBolusTime < minDistance) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed)) + else if (splitted.size == 2 && pump.isSuspended) sendSMS(Sms(receivedSms.phoneNumber, R.string.pumpsuspended)) + else if (splitted.size == 2 || splitted.size == 3) processBOLUS(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "CARBS" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2 || splitted.size == 3) processCARBS(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "CAL" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2) processCAL(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "TARGET" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2) processTARGET(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "SMS" -> + if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed)) + else if (splitted.size == 2) processSMS(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + "HELP" -> + if (splitted.size == 1 || splitted.size == 2) processHELP(splitted, receivedSms) + else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else -> + if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) { + messageToConfirm?.action(splitted[0]) + messageToConfirm = null + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand)) + } + } + send(EventSmsCommunicatorUpdateGui()) + } + + private fun processBG(receivedSms: Sms) { + val actualBG = DatabaseHelper.actualBg() + val lastBG = DatabaseHelper.lastBg() + var reply = "" + val units = ProfileFunctions.getSystemUnits() + if (actualBG != null) { + reply = MainApp.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", " + } else if (lastBG != null) { + val agoMsec = System.currentTimeMillis() - lastBG.date + val agoMin = (agoMsec / 60.0 / 1000.0).toInt() + reply = MainApp.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsToString(units) + " " + String.format(MainApp.gs(R.string.sms_minago), agoMin) + ", " + } + val glucoseStatus = GlucoseStatus.getGlucoseStatusData() + if (glucoseStatus != null) reply += MainApp.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", " + TreatmentsPlugin.getPlugin().updateTotalIOBTreatments() + val bolusIob = TreatmentsPlugin.getPlugin().lastCalculationTreatments.round() + TreatmentsPlugin.getPlugin().updateTotalIOBTempBasals() + val basalIob = TreatmentsPlugin.getPlugin().lastCalculationTempBasals.round() + val cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "SMS COB") + reply += (MainApp.gs(R.string.sms_iob) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" + + MainApp.gs(R.string.sms_bolus) + " " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U " + + MainApp.gs(R.string.sms_basal) + " " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U), " + + MainApp.gs(R.string.cob) + ": " + cobInfo.generateCOBString()) + sendSMS(Sms(receivedSms.phoneNumber, reply)) + receivedSms.processed = true + } + + private fun processLOOP(splitted: Array, receivedSms: Sms) { + when (splitted[1].toUpperCase(Locale.getDefault())) { + "DISABLE", "STOP" -> { + val loopPlugin = LoopPlugin.getPlugin() + if (loopPlugin.isEnabled(PluginType.LOOP)) { + loopPlugin.setPluginEnabled(PluginType.LOOP, false) + ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + send(EventRefreshOverview("SMS_LOOP_STOP")) + val replyText = MainApp.gs(R.string.smscommunicator_loophasbeendisabled) + " " + + MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + }) + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisdisabled)) + receivedSms.processed = true + } + "ENABLE", "START" -> { + val loopPlugin = LoopPlugin.getPlugin() + if (!loopPlugin.isEnabled(PluginType.LOOP)) { + loopPlugin.setPluginEnabled(PluginType.LOOP, true) + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loophasbeenenabled)) + send(EventRefreshOverview("SMS_LOOP_START")) + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisenabled)) + receivedSms.processed = true + } + "STATUS" -> { + val loopPlugin = LoopPlugin.getPlugin() + val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) { + if (loopPlugin.isSuspended()) String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) + else MainApp.gs(R.string.smscommunicator_loopisenabled) + } else + MainApp.gs(R.string.smscommunicator_loopisdisabled) + sendSMS(Sms(receivedSms.phoneNumber, reply)) + receivedSms.processed = true + } + "RESUME" -> { + LoopPlugin.getPlugin().suspendTo(0) + send(EventRefreshOverview("SMS_LOOP_RESUME")) + LoopPlugin.getPlugin().createOfflineEvent(0) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopresumed)) + } + "SUSPEND" -> { + var duration = 0 + if (splitted.size == 3) duration = SafeParse.stringToInt(splitted[2]) + duration = Math.max(0, duration) + duration = Math.min(180, duration) + if (duration == 0) { + receivedSms.processed = true + sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_wrongduration)) + return + } else { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(duration) { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + if (result.success) { + LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger() * 60L * 1000) + LoopPlugin.getPlugin().createOfflineEvent(anInteger() * 60) + send(EventRefreshOverview("SMS_LOOP_SUSPENDED")) + val replyText = MainApp.gs(R.string.smscommunicator_loopsuspended) + " " + + MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } + else -> sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + } + + private fun processTREATMENTS(splitted: Array, receivedSms: Sms) { + if (splitted[1].toUpperCase(Locale.getDefault()) == "REFRESH") { + TreatmentsPlugin.getPlugin().service.resetTreatments() + send(EventNSClientRestart()) + sendSMS(Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT")) + receivedSms.processed = true + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processNSCLIENT(splitted: Array, receivedSms: Sms) { + if (splitted[1].toUpperCase(Locale.getDefault()) == "RESTART") { + send(EventNSClientRestart()) + sendSMS(Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT")) + receivedSms.processed = true + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processHELP(splitted: Array, receivedSms: Sms) { + if (splitted.size == 1) { + sendSMS(Sms(receivedSms.phoneNumber, commands.keys.toString().replace("[", "").replace("]", ""))) + receivedSms.processed = true + } else if (isCommand(splitted[1].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) { + commands[splitted[1].toUpperCase(Locale.getDefault())]?.let { + sendSMS(Sms(receivedSms.phoneNumber, it)) + receivedSms.processed = true + } + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processPUMP(receivedSms: Sms) { + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() { + override fun run() { + val pump = ConfigBuilderPlugin.getPlugin().activePump + if (result.success) { + if (pump != null) { + val reply = pump.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, reply)) + } + } else { + val reply = MainApp.gs(R.string.readstatusfailed) + sendSMS(Sms(receivedSms.phoneNumber, reply)) + } + } + }) + receivedSms.processed = true + } + + private fun processPROFILE(splitted: Array, receivedSms: Sms) { // load profiles + val anInterface = ConfigBuilderPlugin.getPlugin().activeProfileInterface + if (anInterface == null) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.notconfigured)) + receivedSms.processed = true + return + } + val store = anInterface.profile + if (store == null) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.notconfigured)) + receivedSms.processed = true + return + } + val list = store.getProfileList() + if (splitted[1].toUpperCase(Locale.getDefault()) == "STATUS") { + sendSMS(Sms(receivedSms.phoneNumber, ProfileFunctions.getInstance().profileName)) + } else if (splitted[1].toUpperCase(Locale.getDefault()) == "LIST") { + if (list.isEmpty()) sendSMS(Sms(receivedSms.phoneNumber, R.string.invalidprofile)) + else { + var reply = "" + for (i in list.indices) { + if (i > 0) reply += "\n" + reply += (i + 1).toString() + ". " + reply += list[i] + } + sendSMS(Sms(receivedSms.phoneNumber, reply)) + } + } else { + val pindex = SafeParse.stringToInt(splitted[1]) + var percentage = 100 + if (splitted.size > 2) percentage = SafeParse.stringToInt(splitted[2]) + if (pindex > list.size) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (percentage == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (pindex == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { + val profile = store.getSpecificProfile(list[pindex - 1] as String) + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) + else { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_profilereplywithcode), list[pindex - 1], percentage, passCode) + receivedSms.processed = true + val finalPercentage = percentage + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(list[pindex - 1] as String, finalPercentage) { + override fun run() { + ProfileFunctions.doProfileSwitch(store, list[pindex - 1] as String, 0, finalPercentage, 0, DateUtil.now()) + sendSMS(Sms(receivedSms.phoneNumber, R.string.profileswitchcreated)) + } + }) + } + } + } + receivedSms.processed = true + } + + private fun processBASAL(splitted: Array, receivedSms: Sms) { + if (splitted[1].toUpperCase(Locale.getDefault()) == "CANCEL" || splitted[1].toUpperCase(Locale.getDefault()) == "STOP") { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_basalstopreplywithcode), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + if (result.success) { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcanceled) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } else if (splitted[1].endsWith("%")) { + var tempBasalPct = SafeParse.stringToInt(StringUtils.removeEnd(splitted[1], "%")) + var duration = 30 + if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2]) + val profile = ProfileFunctions.getInstance().profile + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) + else if (tempBasalPct == 0 && splitted[1] != "0%") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { + tempBasalPct = MainApp.getConstraintChecker().applyBasalPercentConstraints(Constraint(tempBasalPct), profile).value() + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, object : Callback() { + override fun run() { + if (result.success) { + var replyText: String + replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } else { + var tempBasal = SafeParse.stringToDouble(splitted[1]) + var duration = 30 + if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2]) + val profile = ProfileFunctions.getInstance().profile + if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile)) + else if (tempBasal == 0.0 && splitted[1] != "0") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { + tempBasal = MainApp.getConstraintChecker().applyBasalConstraints(Constraint(tempBasal), profile).value() + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, object : Callback() { + override fun run() { + if (result.success) { + var replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) + else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_tempbasalfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } + } + + private fun processEXTENDED(splitted: Array, receivedSms: Sms) { + if (splitted[1].toUpperCase(Locale.getDefault()) == "CANCEL" || splitted[1].toUpperCase(Locale.getDefault()) == "STOP") { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.cancelExtended(object : Callback() { + override fun run() { + if (result.success) { + var replyText = MainApp.gs(R.string.smscommunicator_extendedcanceled) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_extendedcancelfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } else if (splitted.size != 3) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } else { + var extended = SafeParse.stringToDouble(splitted[1]) + val duration = SafeParse.stringToInt(splitted[2]) + extended = MainApp.getConstraintChecker().applyExtendedBolusConstraints(Constraint(extended)).value() + if (extended == 0.0 || duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(extended, duration) { + override fun run() { + ConfigBuilderPlugin.getPlugin().commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() { + override fun run() { + if (result.success) { + var replyText = String.format(MainApp.gs(R.string.smscommunicator_extendedset), aDouble, duration) + if (Config.APS) replyText += "\n" + MainApp.gs(R.string.loopsuspended) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_extendedfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } + } + + private fun processBOLUS(splitted: Array, receivedSms: Sms) { + var bolus = SafeParse.stringToDouble(splitted[1]) + val isMeal = splitted.size > 2 && splitted[2].equals("MEAL", ignoreCase = true) + bolus = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(bolus)).value() + if (splitted.size == 3 && !isMeal) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } else if (bolus > 0.0) { + val passCode = generatePasscode() + val reply = if (isMeal) + String.format(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode) + else + String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(bolus) { + override fun run() { + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.insulin = aDouble() + detailedBolusInfo.source = Source.USER + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + val resultSuccess = result.success + val resultBolusDelivered = result.bolusDelivered + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() { + override fun run() { + if (resultSuccess) { + var replyText = if (isMeal) + String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered) + else + String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + lastRemoteBolusTime = DateUtil.now() + if (isMeal) { + ProfileFunctions.getInstance().profile?.let { currentProfile -> + var eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration) + eatingSoonTTDuration = + if (eatingSoonTTDuration > 0) eatingSoonTTDuration + else Constants.defaultEatingSoonTTDuration + var eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl) + eatingSoonTT = + if (eatingSoonTT > 0) eatingSoonTT + else if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol + else Constants.defaultEatingSoonTTmgdl + val tempTarget = TempTarget() + .date(System.currentTimeMillis()) + .duration(eatingSoonTTDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(eatingSoonTT, currentProfile.units)) + .high(Profile.toMgdl(eatingSoonTT, currentProfile.units)) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + val tt = if (currentProfile.units == Constants.MMOL) { + DecimalFormatter.to1Decimal(eatingSoonTT) + } else DecimalFormatter.to0Decimal(eatingSoonTT) + replyText += "\n" + String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration) + } + } + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_bolusfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + }) + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processCARBS(splitted: Array, receivedSms: Sms) { + var grams = SafeParse.stringToInt(splitted[1]) + var time = DateUtil.now() + if (splitted.size > 2) { + val seconds = DateUtil.toSeconds(splitted[2].toUpperCase(Locale.getDefault())) + val midnight = MidnightTime.calc() + if (seconds == 0 && (!splitted[2].startsWith("00:00") || !splitted[2].startsWith("12:00"))) { + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + return + } + time = midnight + T.secs(seconds.toLong()).msecs() + } + grams = MainApp.getConstraintChecker().applyCarbsConstraints(Constraint(grams)).value() + if (grams == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + else { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_carbsreplywithcode), grams, DateUtil.timeString(time), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(grams, time) { + override fun run() { + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.carbs = anInteger().toDouble() + detailedBolusInfo.date = secondLong() + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (result.success) { + var replyText = String.format(MainApp.gs(R.string.smscommunicator_carbsset), anInteger) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_carbsfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } + } + }) + } + }) + } + } + + private fun processTARGET(splitted: Array, receivedSms: Sms) { + val isMeal = splitted[1].equals("MEAL", ignoreCase = true) + val isActivity = splitted[1].equals("ACTIVITY", ignoreCase = true) + val isHypo = splitted[1].equals("HYPO", ignoreCase = true) + val isStop = splitted[1].equals("STOP", ignoreCase = true) || splitted[1].equals("CANCEL", ignoreCase = true) + if (isMeal || isActivity || isHypo) { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(Locale.getDefault()), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + val units = ProfileFunctions.getSystemUnits() + var keyDuration = 0 + var defaultTargetDuration = 0 + var keyTarget = 0 + var defaultTargetMMOL = 0.0 + var defaultTargetMGDL = 0.0 + if (isMeal) { + keyDuration = R.string.key_eatingsoon_duration + defaultTargetDuration = Constants.defaultEatingSoonTTDuration + keyTarget = R.string.key_eatingsoon_target + defaultTargetMMOL = Constants.defaultEatingSoonTTmmol + defaultTargetMGDL = Constants.defaultEatingSoonTTmgdl + } else if (isActivity) { + keyDuration = R.string.key_activity_duration + defaultTargetDuration = Constants.defaultActivityTTDuration + keyTarget = R.string.key_activity_target + defaultTargetMMOL = Constants.defaultActivityTTmmol + defaultTargetMGDL = Constants.defaultActivityTTmgdl + } else if (isHypo) { + keyDuration = R.string.key_hypo_duration + defaultTargetDuration = Constants.defaultHypoTTDuration + keyTarget = R.string.key_hypo_target + defaultTargetMMOL = Constants.defaultHypoTTmmol + defaultTargetMGDL = Constants.defaultHypoTTmgdl + } + var ttDuration = SP.getInt(keyDuration, defaultTargetDuration) + ttDuration = if (ttDuration > 0) ttDuration else defaultTargetDuration + var tt = SP.getDouble(keyTarget, if (units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL) + tt = Profile.toCurrentUnits(tt) + tt = if (tt > 0) tt else if (units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL + val tempTarget = TempTarget() + .date(System.currentTimeMillis()) + .duration(ttDuration) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(Profile.toMgdl(tt, units)) + .high(Profile.toMgdl(tt, units)) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + val ttString = if (units == Constants.MMOL) DecimalFormatter.to1Decimal(tt) else DecimalFormatter.to0Decimal(tt) + val replyText = String.format(MainApp.gs(R.string.smscommunicator_tt_set), ttString, ttDuration) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } + }) + } else if (isStop) { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetcancel), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + val tempTarget = TempTarget() + .source(Source.USER) + .date(DateUtil.now()) + .duration(0) + .low(0.0) + .high(0.0) + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget) + val replyText = String.format(MainApp.gs(R.string.smscommunicator_tt_canceled)) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } + }) + } else + sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processSMS(splitted: Array, receivedSms: Sms) { + val isStop = (splitted[1].equals("STOP", ignoreCase = true) + || splitted[1].equals("DISABLE", ignoreCase = true)) + if (isStop) { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_stopsmswithcode), passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() { + override fun run() { + SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false) + val replyText = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms)) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } + }) + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + private fun processCAL(splitted: Array, receivedSms: Sms) { + val cal = SafeParse.stringToDouble(splitted[1]) + if (cal > 0.0) { + val passCode = generatePasscode() + val reply = String.format(MainApp.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode) + receivedSms.processed = true + messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(cal) { + override fun run() { + val result = XdripCalibrations.sendIntent(aDouble) + if (result) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationsent)) else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationfailed)) + } + }) + } else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat)) + } + + fun sendNotificationToAllNumbers(text: String): Boolean { + var result = true + for (i in allowedNumbers.indices) { + val sms = Sms(allowedNumbers[i], text) + result = result && sendSMS(sms) + } + return result + } + + private fun sendSMSToAllNumbers(sms: Sms) { + for (number in allowedNumbers) { + sms.phoneNumber = number + sendSMS(sms) + } + } + + fun sendSMS(sms: Sms): Boolean { + val smsManager = SmsManager.getDefault() + sms.text = stripAccents(sms.text) + try { + if (L.isEnabled(L.SMS)) log.debug("Sending SMS to " + sms.phoneNumber + ": " + sms.text) + if (sms.text.toByteArray().size <= 140) smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null) + else { + val parts = smsManager.divideMessage(sms.text) + smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts, + null, null) + } + messages.add(sms) + } catch (e: IllegalArgumentException) { + return if (e.message == "Invalid message body") { + val notification = Notification(Notification.INVALID_MESSAGE_BODY, MainApp.gs(R.string.smscommunicator_messagebody), Notification.NORMAL) + send(EventNewNotification(notification)) + false + } else { + val notification = Notification(Notification.INVALID_PHONE_NUMBER, MainApp.gs(R.string.smscommunicator_invalidphonennumber), Notification.NORMAL) + send(EventNewNotification(notification)) + false + } + } catch (e: SecurityException) { + val notification = Notification(Notification.MISSING_SMS_PERMISSION, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.NORMAL) + send(EventNewNotification(notification)) + return false + } + send(EventSmsCommunicatorUpdateGui()) + return true + } + + private fun generatePasscode(): String { + val startChar1 = 'A'.toInt() // on iphone 1st char is uppercase :) + var passCode = Character.toString((startChar1 + Math.random() * ('z' - 'a' + 1)).toChar()) + val startChar2: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt() + passCode += Character.toString((startChar2 + Math.random() * ('z' - 'a' + 1)).toChar()) + val startChar3: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt() + passCode += Character.toString((startChar3 + Math.random() * ('z' - 'a' + 1)).toChar()) + passCode = passCode.replace('l', 'k').replace('I', 'J') + return passCode + } + + private fun stripAccents(str: String): String { + var s = str + s = Normalizer.normalize(s, Normalizer.Form.NFD) + s = s.replace("[\\p{InCombiningDiacriticalMarks}]".toRegex(), "") + return s + } + + fun areMoreNumbers(allowednumbers: String?): Boolean { + return allowednumbers?.let { + var countNumbers = 0 + val substrings = it.split(";").toTypedArray() + for (number in substrings) { + var cleaned = number.replace(Regex("\\s+"), "") + if (cleaned.length < 4) continue + cleaned = cleaned.replace("+", "") + cleaned = cleaned.replace("-", "") + if (!cleaned.matches(Regex("[0-9]+"))) continue + countNumbers++ + } + countNumbers > 1 + } ?: false + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt index 78ad0ab37d..e8971ab9d5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.general.tidepool +import android.preference.PreferenceFragment import android.text.Spanned import info.nightscout.androidaps.Constants import info.nightscout.androidaps.MainApp @@ -34,7 +35,6 @@ object TidepoolPlugin : PluginBase(PluginDescription() .preferencesId(R.xml.pref_tidepool) .description(R.string.description_tidepool) ) { - private val log = LoggerFactory.getLogger(L.TIDEPOOL) private var disposable: CompositeDisposable = CompositeDisposable() @@ -111,6 +111,16 @@ object TidepoolPlugin : PluginBase(PluginDescription() super.onStop() } + override fun preprocessPreferences(preferenceFragment: PreferenceFragment) { + super.preprocessPreferences(preferenceFragment) + + val tidepoolTestLogin = preferenceFragment.findPreference(MainApp.gs(R.string.key_tidepool_test_login)) + tidepoolTestLogin?.setOnPreferenceClickListener { + TidepoolUploader.testLogin(preferenceFragment.getActivity()) + false + } + } + private fun doUpload() = when (TidepoolUploader.connectionStatus) { TidepoolUploader.ConnectionStatus.FAILED -> {} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt index ca3c3e0483..4acc0b5d67 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt @@ -19,7 +19,7 @@ class InfoInterceptor(tag: String) : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() - request?.body()?.let { + request.body?.let { if (L.isEnabled(L.TIDEPOOL)) { log.debug("Interceptor Body size: " + it.contentLength()) val requestBuffer = Buffer() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt index 40473c0d93..5a65206f83 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt @@ -14,9 +14,9 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.OKDialog import info.nightscout.androidaps.utils.SP import info.nightscout.androidaps.utils.T -import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient -import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.logging.HttpLoggingInterceptor import org.slf4j.LoggerFactory import retrofit2.Retrofit @@ -43,7 +43,7 @@ object TidepoolUploader { val PUMPTYPE = "Tandem" - var connectionStatus: ConnectionStatus = TidepoolUploader.ConnectionStatus.DISCONNECTED + var connectionStatus: ConnectionStatus = ConnectionStatus.DISCONNECTED fun getRetrofitInstance(): Retrofit? { if (retrofit == null) { @@ -75,7 +75,7 @@ object TidepoolUploader { retrofit = null if (L.isEnabled(L.TIDEPOOL)) log.debug("Instance reset") - connectionStatus = TidepoolUploader.ConnectionStatus.DISCONNECTED + connectionStatus = ConnectionStatus.DISCONNECTED } @Synchronized @@ -116,13 +116,13 @@ object TidepoolUploader { val call = session.service?.getLogin(it) call?.enqueue(TidepoolCallback(session, "Login", { - OKDialog.show(rootContext, MainApp.gs(R.string.tidepool), "Successfully logged into Tidepool.", null) + OKDialog.show(rootContext, MainApp.gs(R.string.tidepool), "Successfully logged into Tidepool.") }, { - OKDialog.show(rootContext, MainApp.gs(R.string.tidepool), "Failed to log into Tidepool.\nCheck that your user name and password are correct.", null) + OKDialog.show(rootContext, MainApp.gs(R.string.tidepool), "Failed to log into Tidepool.\nCheck that your user name and password are correct.") })) } - ?: OKDialog.show(rootContext, MainApp.gs(R.string.tidepool), "Cannot do login as user credentials have not been set correctly", null) + ?: OKDialog.show(rootContext, MainApp.gs(R.string.tidepool), "Cannot do login as user credentials have not been set correctly") } @@ -197,7 +197,7 @@ object TidepoolUploader { } else -> { - val body = RequestBody.create(MediaType.parse("application/json"), chunk) + val body = chunk.toRequestBody("application/json".toMediaTypeOrNull()) RxBus.send(EventTidepoolStatus(("Uploading"))) if (session.service != null && session.token != null && session.datasetReply != null) { @@ -231,11 +231,11 @@ object TidepoolUploader { extendWakeLock(60000) val call = session!!.service?.deleteDataSet(session!!.token!!, session!!.datasetReply!!.id!!) call?.enqueue(TidepoolCallback(session!!, "Delete Dataset", { - connectionStatus = TidepoolUploader.ConnectionStatus.DISCONNECTED + connectionStatus = ConnectionStatus.DISCONNECTED RxBus.send(EventTidepoolStatus(("Dataset removed OK"))) releaseWakeLock() }, { - connectionStatus = TidepoolUploader.ConnectionStatus.DISCONNECTED + connectionStatus = ConnectionStatus.DISCONNECTED RxBus.send(EventTidepoolStatus(("Dataset remove FAILED"))) releaseWakeLock() })) @@ -255,11 +255,11 @@ object TidepoolUploader { extendWakeLock(60000) val call = session.service?.deleteAllData(token, userid) call?.enqueue(TidepoolCallback(session, "Delete all data", { - connectionStatus = TidepoolUploader.ConnectionStatus.DISCONNECTED + connectionStatus = ConnectionStatus.DISCONNECTED RxBus.send(EventTidepoolStatus(("All data removed OK"))) releaseWakeLock() }, { - connectionStatus = TidepoolUploader.ConnectionStatus.DISCONNECTED + connectionStatus = ConnectionStatus.DISCONNECTED RxBus.send(EventTidepoolStatus(("All data remove FAILED"))) releaseWakeLock() })) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt index 93009ea7e1..82ae37cebc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt @@ -41,12 +41,12 @@ class ProfileElement private constructor(ps: ProfileSwitch) checkNotNull(profile) for (br in profile.basalValues) basalSchedules.Normal.add(BasalRate(br.timeAsSeconds * 1000, br.value)) - for (target in profile.singleTargets) - bgTargets.Normal.add(Target(target.timeAsSeconds * 1000, Profile.toMgdl(target.value, profile.units))) + for (target in profile.singleTargetsMgdl) + bgTargets.Normal.add(Target(target.timeAsSeconds * 1000, target.value)) for (ic in profile.ics) carbRatios.Normal.add(Ratio(ic.timeAsSeconds * 1000, ic.value)) - for (isf in profile.isfs) - insulinSensitivities.Normal.add(Ratio(isf.timeAsSeconds * 1000, Profile.toMgdl(isf.value, profile.units))) + for (isf in profile.isfsMgdl) + insulinSensitivities.Normal.add(Ratio(isf.timeAsSeconds * 1000, isf.value)) } inner class BasalProfile internal constructor( diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt index e7854f8ff2..f9e2b724cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt @@ -3,9 +3,9 @@ package info.nightscout.androidaps.plugins.general.tidepool.events import info.nightscout.androidaps.events.Event import info.nightscout.androidaps.logging.L import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.LocaleHelper import org.slf4j.LoggerFactory import java.text.SimpleDateFormat +import java.util.* class EventTidepoolStatus(val status: String) : Event() { private val log = LoggerFactory.getLogger(L.TIDEPOOL) @@ -17,7 +17,7 @@ class EventTidepoolStatus(val status: String) : Event() { log.debug("New status: $status") } - private var timeFormat = SimpleDateFormat("HH:mm:ss", LocaleHelper.currentLocale()) + private var timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault()) fun toPreparedHtml(): StringBuilder { val stringBuilder = StringBuilder() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/BaseMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/BaseMessage.kt index d3cdffcf8c..d9dfc97b38 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/BaseMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/BaseMessage.kt @@ -1,8 +1,9 @@ package info.nightscout.androidaps.plugins.general.tidepool.messages import info.nightscout.androidaps.plugins.general.tidepool.utils.GsonInstance -import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody open class BaseMessage { private fun toS(): String { @@ -10,7 +11,7 @@ open class BaseMessage { } fun getBody(): RequestBody { - return RequestBody.create(MediaType.parse("application/json"), this.toS()) + return this.toS().toRequestBody("application/json".toMediaTypeOrNull()) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.java index 1cf4dd7539..0fd820a4da 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.java @@ -131,12 +131,7 @@ public class ActionStringHandler { ///////////////////////////////////////////////////////// TEMPTARGET boolean isMGDL = Boolean.parseBoolean(act[1]); - Profile profile = ProfileFunctions.getInstance().getProfile(); - if (profile == null) { - sendError("No profile found!"); - return; - } - if (profile.getUnits().equals(Constants.MGDL) != isMGDL) { + if (ProfileFunctions.getSystemUnits().equals(Constants.MGDL) != isMGDL) { sendError("Different units used on watch and phone!"); return; } @@ -222,7 +217,7 @@ public class ActionStringHandler { DecimalFormat format = new DecimalFormat("0.00"); DecimalFormat formatInt = new DecimalFormat("0"); BolusWizard bolusWizard = new BolusWizard(profile, profileName, TreatmentsPlugin.getPlugin().getTempTargetFromHistory(), - carbsAfterConstraints, cobInfo.displayCob, bgReading.valueToUnits(profile.getUnits()), + carbsAfterConstraints, cobInfo.displayCob, bgReading.valueToUnits(ProfileFunctions.getSystemUnits()), 0d, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend); if (Math.abs(bolusWizard.getInsulinAfterConstraints() - bolusWizard.getCalculatedTotalInsulin()) >= 0.01) { @@ -507,8 +502,8 @@ public class ActionStringHandler { if (LoopPlugin.lastRun.lastAPSRun != null) ret += "\nLast Run: " + DateUtil.timeString(LoopPlugin.lastRun.lastAPSRun); - if (LoopPlugin.lastRun.lastEnact != null) - ret += "\nLast Enact: " + DateUtil.timeString(LoopPlugin.lastRun.lastEnact); + if (LoopPlugin.lastRun.lastTBREnact != 0) + ret += "\nLast Enact: " + DateUtil.timeString(LoopPlugin.lastRun.lastTBREnact); } @@ -534,14 +529,14 @@ public class ActionStringHandler { //Check for Temp-Target: TempTarget tempTarget = TreatmentsPlugin.getPlugin().getTempTargetFromHistory(); if (tempTarget != null) { - ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.low, tempTarget.low, Constants.MGDL, profile.getUnits()); + ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.low, tempTarget.low, Constants.MGDL, ProfileFunctions.getSystemUnits()); ret += "\nuntil: " + DateUtil.timeString(tempTarget.originalEnd()); ret += "\n\n"; } ret += "DEFAULT RANGE: "; - ret += profile.getTargetLow() + " - " + profile.getTargetHigh(); - ret += " target: " + profile.getTarget(); + ret += Profile.fromMgdlToUnits(profile.getTargetLowMgdl(), ProfileFunctions.getSystemUnits()) + " - " + Profile.fromMgdlToUnits(profile.getTargetHighMgdl(), ProfileFunctions.getSystemUnits()); + ret += " target: " + Profile.fromMgdlToUnits(profile.getTargetMgdl(), ProfileFunctions.getSystemUnits()); return ret; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java index 7eebdc8d8e..2293473338 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java @@ -1,12 +1,5 @@ package info.nightscout.androidaps.plugins.general.wear.wearintegration; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -17,9 +10,10 @@ import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.preference.PreferenceManager; -import androidx.annotation.NonNull; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.wearable.CapabilityApi; @@ -32,31 +26,37 @@ import com.google.android.gms.wearable.PutDataRequest; import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.WearableListenerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler; import info.nightscout.androidaps.plugins.general.wear.WearPlugin; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.plugins.treatments.Treatment; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.SafeParse; import info.nightscout.androidaps.utils.ToastUtils; public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { @@ -88,7 +88,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog boolean wear_integration = false; - SharedPreferences mPrefs; private static boolean lastLoopStatus; private static Logger log = LoggerFactory.getLogger(WatchUpdaterService.class); @@ -108,7 +107,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog @Override public void onCreate() { - mPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); listenForChangeInSettings(); setSettings(); if (wear_integration) { @@ -139,7 +137,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog googleApiClient.disconnect(); } googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this) - .addOnConnectionFailedListener(this).addApi(Wearable.API).build(); + .addOnConnectionFailedListener(this).addApi(Wearable.API).build(); Wearable.MessageApi.addListener(googleApiClient, this); if (googleApiClient.isConnected()) { log.debug(logPrefix + "API client is connected"); @@ -303,10 +301,10 @@ public class WatchUpdaterService extends WearableListenerService implements Goog private DataMap dataMapSingleBG(BgReading lastBG, GlucoseStatus glucoseStatus) { - String units = ProfileFunctions.getInstance().getProfileUnits(); + String units = ProfileFunctions.getSystemUnits(); - Double lowLine = SafeParse.stringToDouble(mPrefs.getString("low_mark", "0")); - Double highLine = SafeParse.stringToDouble(mPrefs.getString("high_mark", "0")); + Double lowLine = OverviewPlugin.INSTANCE.determineLowLine(); + Double highLine = OverviewPlugin.INSTANCE.determineHighLine(); // convert to mg/dl if (!units.equals(Constants.MGDL)) { @@ -323,7 +321,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog highLine = OverviewPlugin.INSTANCE.getBgTargetHigh(); } - long sgvLevel = 0l; + long sgvLevel = 0L; if (lastBG.value > highLine) { sgvLevel = 1; } else if (lastBG.value < lowLine) { @@ -721,7 +719,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog //bgi - double bgi = -(bolusIob.activity + basalIob.activity) * 5 * profile.getIsf(); + double bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.getIsfMgdl(), ProfileFunctions.getSystemUnits()); bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.to1Decimal(bgi); status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString); @@ -737,7 +735,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog //OpenAPS status if (Config.APS) { //we are AndroidAPS - openApsStatus = LoopPlugin.lastRun != null && LoopPlugin.lastRun.lastEnact != null && LoopPlugin.lastRun.lastEnact.getTime() != 0 ? LoopPlugin.lastRun.lastEnact.getTime() : -1; + openApsStatus = LoopPlugin.lastRun != null && LoopPlugin.lastRun.lastTBREnact != 0 ? LoopPlugin.lastRun.lastTBREnact : -1; } else { //NSClient or remote openApsStatus = NSDeviceStatus.getOpenApsTimestamp(); @@ -748,14 +746,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog dataMapRequest.getDataMap().putString("externalStatusString", status); dataMapRequest.getDataMap().putString("iobSum", iobSum); dataMapRequest.getDataMap().putString("iobDetail", iobDetail); - dataMapRequest.getDataMap().putBoolean("detailedIob", mPrefs.getBoolean("wear_detailediob", false)); + dataMapRequest.getDataMap().putBoolean("detailedIob", SP.getBoolean(R.string.key_wear_detailediob, false)); dataMapRequest.getDataMap().putString("cob", cobString); dataMapRequest.getDataMap().putString("currentBasal", currentBasal); dataMapRequest.getDataMap().putString("battery", "" + phoneBattery); dataMapRequest.getDataMap().putString("rigBattery", rigBattery); dataMapRequest.getDataMap().putLong("openApsStatus", openApsStatus); dataMapRequest.getDataMap().putString("bgi", bgiString); - dataMapRequest.getDataMap().putBoolean("showBgi", mPrefs.getBoolean("wear_showbgi", false)); + dataMapRequest.getDataMap().putBoolean("showBgi", SP.getBoolean(R.string.key_wear_showbgi, false)); dataMapRequest.getDataMap().putInt("batteryLevel", (phoneBattery >= 30) ? 1 : 0); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); debugData("sendStatus", putDataRequest); @@ -789,7 +787,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog private void executeTask(AsyncTask task, DataMap... parameters) { - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])parameters); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[]) parameters); // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); // } else { @@ -818,7 +816,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog } String iobString = ""; - if (mPrefs.getBoolean("wear_detailediob", false)) { + if (SP.getBoolean(R.string.key_wear_detailediob, false)) { iobString = iobSum + " " + iobDetail; } else { iobString = iobSum + "U"; @@ -827,7 +825,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog status += currentBasal + " " + iobString; //add BGI if shown, otherwise return - if (mPrefs.getBoolean("wear_showbgi", false)) { + if (SP.getBoolean(R.string.key_wear_showbgi, false)) { status += " " + bgiString; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatuslinePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatuslinePlugin.java index da61b83b98..b810624e87 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatuslinePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatuslinePlugin.java @@ -206,7 +206,7 @@ public class StatuslinePlugin extends PluginBase { return status; } - double bgi = -(bolusIob.activity + basalIob.activity) * 5 * profile.getIsf(); + double bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.getIsfMgdl(), ProfileFunctions.getSystemUnits()); status += " " + ((bgi >= 0) ? "+" : "") + DecimalFormatter.to2Decimal(bgi); status += " " + IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "StatuslinePlugin").generateCOBString(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.java index 8954c81ce7..dec9239948 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.java @@ -52,7 +52,7 @@ public class InsulinFragment extends Fragment { private void updateGUI() { insulinName.setText(ConfigBuilderPlugin.getPlugin().getActiveInsulin().getFriendlyName()); insulinComment.setText(ConfigBuilderPlugin.getPlugin().getActiveInsulin().getComment()); - insulinDia.setText(MainApp.gs(R.string.dia) + " " + ConfigBuilderPlugin.getPlugin().getActiveInsulin().getDia() + "h"); + insulinDia.setText(MainApp.gs(R.string.dia) + ": " + ConfigBuilderPlugin.getPlugin().getActiveInsulin().getDia() + "h"); insulinGraph.show(ConfigBuilderPlugin.getPlugin().getActiveInsulin()); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensData.java index 4b47602f33..0c93e0266f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensData.java @@ -48,7 +48,7 @@ public class AutosensData implements DataPointWithLabelInterface { if (SensitivityAAPSPlugin.getPlugin().isEnabled(PluginType.SENSITIVITY) || SensitivityWeightedAveragePlugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) { double maxAbsorptionHours = SP.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME); Profile profile = ProfileFunctions.getInstance().getProfile(t.date); - double sens = Profile.toMgdl(profile.getIsf(t.date), profile.getUnits()); + double sens = profile.getIsfMgdl(t.date); double ic = profile.getIc(t.date); min5minCarbImpact = t.carbs / (maxAbsorptionHours * 60 / 5) * sens / ic; if (L.isEnabled(L.AUTOSENS)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.java b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.java index 07d1b52947..9fe87856b7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.java @@ -149,7 +149,7 @@ public class IobCobOref1Thread extends Thread { if (L.isEnabled(L.AUTOSENS)) log.debug("Processing calculation thread: " + from + " (" + i + "/" + bucketed_data.size() + ")"); - double sens = Profile.toMgdl(profile.getIsf(bgTime), profile.getUnits()); + double sens = profile.getIsfMgdl(bgTime); AutosensData autosensData = new AutosensData(); autosensData.time = bgTime; @@ -237,11 +237,11 @@ public class IobCobOref1Thread extends Thread { } } - List recentTreatments = TreatmentsPlugin.getPlugin().getTreatments5MinBackFromHistory(bgTime); - for (int ir = 0; ir < recentTreatments.size(); ir++) { - autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; - autosensData.activeCarbsList.add(new AutosensData.CarbsInPast(recentTreatments.get(ir))); - autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentTreatments.get(ir).carbs) + "g]"; + List recentCarbTreatments = TreatmentsPlugin.getPlugin().getCarbTreatments5MinBackFromHistory(bgTime); + for (Treatment recentCarbTreatment : recentCarbTreatments) { + autosensData.carbsFromBolus += recentCarbTreatment.carbs; + autosensData.activeCarbsList.add(new AutosensData.CarbsInPast(recentCarbTreatment)); + autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.carbs) + "g]"; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.java index a8ecc2a06a..7389e39b8d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.java @@ -148,7 +148,7 @@ public class IobCobThread extends Thread { if (L.isEnabled(L.AUTOSENS)) log.debug("Processing calculation thread: " + from + " (" + i + "/" + bucketed_data.size() + ")"); - double sens = Profile.toMgdl(profile.getIsf(bgTime), profile.getUnits()); + double sens = profile.getIsfMgdl(bgTime); AutosensData autosensData = new AutosensData(); autosensData.time = bgTime; @@ -236,11 +236,11 @@ public class IobCobThread extends Thread { } } - List recentTreatments = TreatmentsPlugin.getPlugin().getTreatments5MinBackFromHistory(bgTime); - for (int ir = 0; ir < recentTreatments.size(); ir++) { - autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; - autosensData.activeCarbsList.add(new AutosensData.CarbsInPast(recentTreatments.get(ir))); - autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentTreatments.get(ir).carbs) + "g]"; + List recentCarbTreatments = TreatmentsPlugin.getPlugin().getCarbTreatments5MinBackFromHistory(bgTime); + for (Treatment recentCarbTreatment : recentCarbTreatments) { + autosensData.carbsFromBolus += recentCarbTreatment.carbs; + autosensData.activeCarbsList.add(new AutosensData.CarbsInPast(recentCarbTreatment)); + autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.carbs) + "g]"; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.java deleted file mode 100644 index f8969e9594..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.java +++ /dev/null @@ -1,212 +0,0 @@ -package info.nightscout.androidaps.plugins.profile.local; - - -import android.app.Activity; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.RadioButton; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - -import java.text.DecimalFormat; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ProfileStore; -import info.nightscout.androidaps.events.EventInitializationChanged; -import info.nightscout.androidaps.interfaces.PumpDescription; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment; -import info.nightscout.androidaps.plugins.general.careportal.Dialogs.NewNSTreatmentDialog; -import info.nightscout.androidaps.plugins.general.careportal.OptionsToShow; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.NumberPicker; -import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.utils.TimeListEdit; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -import static info.nightscout.androidaps.plugins.insulin.InsulinOrefBasePlugin.MIN_DIA; - -public class LocalProfileFragment extends Fragment { - private CompositeDisposable disposable = new CompositeDisposable(); - - private NumberPicker diaView; - private RadioButton mgdlView; - private RadioButton mmolView; - private TimeListEdit basalView; - private Button profileswitchButton; - private Button resetButton; - private Button saveButton; - - private TextView invalidProfile; - - private Runnable save = () -> { - doEdit(); - if (basalView != null) { - basalView.updateLabel(MainApp.gs(R.string.nsprofileview_basal_label) + ": " + getSumLabel()); - } - }; - - private TextWatcher textWatch = 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) { - LocalProfilePlugin.getPlugin().dia = SafeParse.stringToDouble(diaView.getText().toString()); - doEdit(); - } - }; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - PumpDescription pumpDescription = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription(); - View layout = inflater.inflate(R.layout.localprofile_fragment, container, false); - diaView = layout.findViewById(R.id.localprofile_dia); - diaView.setParams(LocalProfilePlugin.getPlugin().dia, MIN_DIA, 12d, 0.1d, new DecimalFormat("0.0"), false, layout.findViewById(R.id.localprofile_save), textWatch); - mgdlView = layout.findViewById(R.id.localprofile_mgdl); - mmolView = layout.findViewById(R.id.localprofile_mmol); - new TimeListEdit(getContext(), layout, R.id.localprofile_ic, MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.getPlugin().ic, null, 0.5, 50d, 0.1d, new DecimalFormat("0.0"), save); - new TimeListEdit(getContext(), layout, R.id.localprofile_isf, MainApp.gs(R.string.nsprofileview_isf_label) + ":", LocalProfilePlugin.getPlugin().isf, null, 0.5, 500d, 0.1d, new DecimalFormat("0.0"), save); - basalView = new TimeListEdit(getContext(), layout, R.id.localprofile_basal, MainApp.gs(R.string.nsprofileview_basal_label) + ": " + getSumLabel(), LocalProfilePlugin.getPlugin().basal, null, pumpDescription.basalMinimumRate, 10, 0.01d, new DecimalFormat("0.00"), save); - new TimeListEdit(getContext(), layout, R.id.localprofile_target, MainApp.gs(R.string.nsprofileview_target_label) + ":", LocalProfilePlugin.getPlugin().targetLow, LocalProfilePlugin.getPlugin().targetHigh, 3d, 200, 0.1d, new DecimalFormat("0.0"), save); - profileswitchButton = layout.findViewById(R.id.localprofile_profileswitch); - resetButton = layout.findViewById(R.id.localprofile_reset); - saveButton = layout.findViewById(R.id.localprofile_save); - - - invalidProfile = layout.findViewById(R.id.invalidprofile); - - if (!ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().isTempBasalCapable) { - layout.findViewById(R.id.localprofile_basal).setVisibility(View.GONE); - } - - mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl); - mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol); - - mgdlView.setOnClickListener(v -> { - LocalProfilePlugin.getPlugin().mgdl = mgdlView.isChecked(); - LocalProfilePlugin.getPlugin().mmol = !LocalProfilePlugin.getPlugin().mgdl; - mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol); - doEdit(); - }); - mmolView.setOnClickListener(v -> { - LocalProfilePlugin.getPlugin().mmol = mmolView.isChecked(); - LocalProfilePlugin.getPlugin().mgdl = !LocalProfilePlugin.getPlugin().mmol; - mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl); - doEdit(); - }); - - profileswitchButton.setOnClickListener(view -> { - NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); - final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCHDIRECT; - profileswitch.executeProfileSwitch = true; - newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); - newDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); - }); - - resetButton.setOnClickListener(view -> { - LocalProfilePlugin.getPlugin().loadSettings(); - mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl); - mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol); - diaView.setParams(LocalProfilePlugin.getPlugin().dia, MIN_DIA, 12d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.localprofile_save), textWatch); - new TimeListEdit(getContext(), layout, R.id.localprofile_ic, MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.getPlugin().ic, null, 0.5, 50d, 0.1d, new DecimalFormat("0.0"), save); - new TimeListEdit(getContext(), layout, R.id.localprofile_isf, MainApp.gs(R.string.nsprofileview_isf_label) + ":", LocalProfilePlugin.getPlugin().isf, null, 0.5, 500d, 0.1d, new DecimalFormat("0.0"), save); - basalView = new TimeListEdit(getContext(), layout, R.id.localprofile_basal, MainApp.gs(R.string.nsprofileview_basal_label) + ": " + getSumLabel(), LocalProfilePlugin.getPlugin().basal, null, pumpDescription.basalMinimumRate, 10, 0.01d, new DecimalFormat("0.00"), save); - new TimeListEdit(getContext(), layout, R.id.localprofile_target, MainApp.gs(R.string.nsprofileview_target_label) + ":", LocalProfilePlugin.getPlugin().targetLow, LocalProfilePlugin.getPlugin().targetHigh, 3d, 200, 0.1d, new DecimalFormat("0.0"), save); - updateGUI(); - }); - - saveButton.setOnClickListener(view -> { - if (!LocalProfilePlugin.getPlugin().isValidEditState()) { - return; //Should not happen as saveButton should not be visible if not valid - } - LocalProfilePlugin.getPlugin().storeSettings(); - updateGUI(); - }); - - return layout; - } - - @Override - public synchronized void onResume() { - super.onResume(); - disposable.add(RxBus.INSTANCE - .toObservable(EventInitializationChanged.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> updateGUI(), FabricPrivacy::logException) - ); - updateGUI(); - } - - @Override - public synchronized void onPause() { - super.onPause(); - disposable.clear(); - } - - public void doEdit() { - LocalProfilePlugin.getPlugin().setEdited(true); - updateGUI(); - } - - @NonNull - public String getSumLabel() { - ProfileStore profile = LocalProfilePlugin.getPlugin().createProfileStore(); - if (profile != null) - return " ∑" + DecimalFormatter.to2Decimal(profile.getDefaultProfile().baseBasalSum()) + MainApp.gs(R.string.insulin_unit_shortname); - else - return MainApp.gs(R.string.localprofile); - } - - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - boolean isValid = LocalProfilePlugin.getPlugin().isValidEditState(); - boolean isEdited = LocalProfilePlugin.getPlugin().isEdited(); - if (isValid) { - invalidProfile.setVisibility(View.GONE); //show invalid profile - - if (isEdited) { - //edited profile -> save first - profileswitchButton.setVisibility(View.GONE); - saveButton.setVisibility(View.VISIBLE); - } else { - profileswitchButton.setVisibility(View.VISIBLE); - saveButton.setVisibility(View.GONE); - } - } else { - invalidProfile.setVisibility(View.VISIBLE); - profileswitchButton.setVisibility(View.GONE); - saveButton.setVisibility(View.GONE); //don't save an invalid profile - } - - //Show reset button iff data was edited - if (isEdited) { - resetButton.setVisibility(View.VISIBLE); - } else { - resetButton.setVisibility(View.GONE); - } - }); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt new file mode 100644 index 0000000000..4478c025d4 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt @@ -0,0 +1,261 @@ +package info.nightscout.androidaps.plugins.profile.local + +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.fragment.app.Fragment +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.dialogs.ProfileSwitchDialog +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.insulin.InsulinOrefBasePlugin.MIN_DIA +import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged +import info.nightscout.androidaps.utils.* +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.localprofile_fragment.* +import java.text.DecimalFormat + +class LocalProfileFragment : Fragment() { + private var disposable: CompositeDisposable = CompositeDisposable() + + private var basalView: TimeListEdit? = null + private var spinner: SpinnerHelper? = null + + private val save = Runnable { + doEdit() + basalView?.updateLabel(MainApp.gs(R.string.nsprofileview_basal_label) + ": " + sumLabel()) + } + + private val textWatch = object : TextWatcher { + override fun afterTextChanged(s: Editable) {} + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + LocalProfilePlugin.currentProfile().dia = SafeParse.stringToDouble(localprofile_dia.text.toString()) + LocalProfilePlugin.currentProfile().name = localprofile_name.text.toString() + doEdit() + } + } + + private fun sumLabel(): String { + val profile = LocalProfilePlugin.createProfileStore().getDefaultProfile() + val sum = profile?.baseBasalSum() ?: 0.0 + return " ∑" + DecimalFormatter.to2Decimal(sum) + MainApp.gs(R.string.insulin_unit_shortname) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.localprofile_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + // activate DIA tab + processVisibilityOnClick(dia_tab) + localprofile_dia_placeholder.visibility = View.VISIBLE + // setup listeners + dia_tab.setOnClickListener { + processVisibilityOnClick(it) + localprofile_dia_placeholder.visibility = View.VISIBLE + } + ic_tab.setOnClickListener { + processVisibilityOnClick(it) + localprofile_ic.visibility = View.VISIBLE + } + isf_tab.setOnClickListener { + processVisibilityOnClick(it) + localprofile_isf.visibility = View.VISIBLE + } + basal_tab.setOnClickListener { + processVisibilityOnClick(it) + localprofile_basal.visibility = View.VISIBLE + } + target_tab.setOnClickListener { + processVisibilityOnClick(it) + localprofile_target.visibility = View.VISIBLE + } + } + + fun build() { + val pumpDescription = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription ?: return + val units = if (LocalProfilePlugin.currentProfile().mgdl) Constants.MGDL else Constants.MMOL + + localprofile_name.removeTextChangedListener(textWatch) + localprofile_name.setText(LocalProfilePlugin.currentProfile().name) + localprofile_name.addTextChangedListener(textWatch) + localprofile_dia.setParams(LocalProfilePlugin.currentProfile().dia, HardLimits.MINDIA, HardLimits.MAXDIA, 0.1, DecimalFormat("0.0"), false, localprofile_save, textWatch) + localprofile_dia.setTag("LP_DIA") + TimeListEdit(context, view, R.id.localprofile_ic, "IC", MainApp.gs(R.string.nsprofileview_ic_label), LocalProfilePlugin.currentProfile().ic, null, HardLimits.MINIC, HardLimits.MAXIC, 0.1, DecimalFormat("0.0"), save) + basalView = TimeListEdit(context, view, R.id.localprofile_basal, "BASAL", MainApp.gs(R.string.nsprofileview_basal_label) + ": " + sumLabel(), LocalProfilePlugin.currentProfile().basal, null, pumpDescription.basalMinimumRate, 10.0, 0.01, DecimalFormat("0.00"), save) + if (units == Constants.MGDL) { + TimeListEdit(context, view, R.id.localprofile_isf, "ISF", MainApp.gs(R.string.nsprofileview_isf_label), LocalProfilePlugin.currentProfile().isf, null, HardLimits.MINISF, HardLimits.MAXISF, 1.0, DecimalFormat("0"), save) + TimeListEdit(context, view, R.id.localprofile_target, "TARGET", MainApp.gs(R.string.nsprofileview_target_label), LocalProfilePlugin.currentProfile().targetLow, LocalProfilePlugin.currentProfile().targetHigh, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble(), 1.0, DecimalFormat("0"), save) + } else { + TimeListEdit(context, view, R.id.localprofile_isf, "ISF", MainApp.gs(R.string.nsprofileview_isf_label), LocalProfilePlugin.currentProfile().isf, null, Profile.fromMgdlToUnits(HardLimits.MINISF, Constants.MMOL), Profile.fromMgdlToUnits(HardLimits.MAXISF, Constants.MMOL), 0.1, DecimalFormat("0.0"), save) + TimeListEdit(context, view, R.id.localprofile_target, "TARGET", MainApp.gs(R.string.nsprofileview_target_label), LocalProfilePlugin.currentProfile().targetLow, LocalProfilePlugin.currentProfile().targetHigh, Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), Constants.MMOL), Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble(), Constants.MMOL), 0.1, DecimalFormat("0.0"), save) + } + + // Spinner + spinner = SpinnerHelper(view?.findViewById(R.id.localprofile_spinner)) + val profileList: ArrayList = LocalProfilePlugin.profile?.getProfileList() + ?: ArrayList() + context?.let { context -> + val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) + spinner?.adapter = adapter + spinner?.setSelection(LocalProfilePlugin.currentProfileIndex) + } ?: return + spinner?.setOnItemSelectedListener(object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { + } + + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + if (LocalProfilePlugin.isEdited) { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.doyouwantswitchprofile), Runnable { + LocalProfilePlugin.currentProfileIndex = position + build() + }, Runnable { + spinner?.setSelection(LocalProfilePlugin.currentProfileIndex) + }) + } + } else { + LocalProfilePlugin.currentProfileIndex = position + build() + } + } + }) + + localprofile_profile_add.setOnClickListener { + if (LocalProfilePlugin.isEdited) { + activity?.let { OKDialog.show(it, "", MainApp.gs(R.string.saveorresetchangesfirst)) } + } else { + LocalProfilePlugin.addNewProfile() + build() + } + } + + localprofile_profile_clone.setOnClickListener { + if (LocalProfilePlugin.isEdited) { + activity?.let { OKDialog.show(it, "", MainApp.gs(R.string.saveorresetchangesfirst)) } + } else { + LocalProfilePlugin.cloneProfile() + build() + } + } + + localprofile_profile_remove.setOnClickListener { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.deletecurrentprofile), Runnable { + LocalProfilePlugin.removeCurrentProfile() + build() + }, null) + } + } + + // this is probably not possible because it leads to invalid profile + // if (!pumpDescription.isTempBasalCapable) localprofile_basal.visibility = View.GONE + + @Suppress("SETTEXTL18N") + localprofile_units.text = MainApp.gs(R.string.units_colon) + " " + (if (LocalProfilePlugin.currentProfile().mgdl) MainApp.gs(R.string.mgdl) else MainApp.gs(R.string.mmol)) + + localprofile_profileswitch.setOnClickListener { + // TODO: select in dialog LocalProfilePlugin.currentProfileIndex + fragmentManager?.let { ProfileSwitchDialog().show(it, "NewNSTreatmentDialog") } + } + + localprofile_reset.setOnClickListener { + LocalProfilePlugin.loadSettings() + @Suppress("SETTEXTL18N") + localprofile_units.text = MainApp.gs(R.string.units_colon) + " " + (if (LocalProfilePlugin.currentProfile().mgdl) MainApp.gs(R.string.mgdl) else MainApp.gs(R.string.mmol)) + localprofile_dia.setParams(LocalProfilePlugin.currentProfile().dia, MIN_DIA, 12.0, 0.1, DecimalFormat("0.0"), false, localprofile_save, textWatch) + localprofile_dia.setTag("LP_DIA") + TimeListEdit(context, view, R.id.localprofile_ic, "IC", MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.currentProfile().ic, null, 0.5, 50.0, 0.1, DecimalFormat("0.0"), save) + TimeListEdit(context, view, R.id.localprofile_isf, "ISF", MainApp.gs(R.string.nsprofileview_isf_label) + ":", LocalProfilePlugin.currentProfile().isf, null, 0.5, 500.0, 0.1, DecimalFormat("0.0"), save) + basalView = TimeListEdit(context, view, R.id.localprofile_basal, "BASAL", MainApp.gs(R.string.nsprofileview_basal_label) + ": " + sumLabel(), LocalProfilePlugin.currentProfile().basal, null, pumpDescription.basalMinimumRate, 10.0, 0.01, DecimalFormat("0.00"), save) + TimeListEdit(context, view, R.id.localprofile_target, "TARGET", MainApp.gs(R.string.nsprofileview_target_label) + ":", LocalProfilePlugin.currentProfile().targetLow, LocalProfilePlugin.currentProfile().targetHigh, 3.0, 200.0, 0.1, DecimalFormat("0.0"), save) + updateGUI() + } + + localprofile_save.setOnClickListener { + if (!LocalProfilePlugin.isValidEditState()) { + return@setOnClickListener //Should not happen as saveButton should not be visible if not valid + } + LocalProfilePlugin.storeSettings(activity) + build() + } + updateGUI() + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable.add(RxBus + .toObservable(EventLocalProfileChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ build() }, { FabricPrivacy.logException(it) }) + ) + build() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + fun doEdit() { + LocalProfilePlugin.isEdited = true + updateGUI() + } + + fun updateGUI() { + if (localprofile_profileswitch == null) return + val isValid = LocalProfilePlugin.isValidEditState() + val isEdited = LocalProfilePlugin.isEdited + if (isValid) { + this.view?.setBackgroundColor(MainApp.gc(R.color.ok_background)) + + if (isEdited) { + //edited profile -> save first + localprofile_profileswitch.visibility = View.GONE + localprofile_save.visibility = View.VISIBLE + } else { + localprofile_profileswitch.visibility = View.VISIBLE + localprofile_save.visibility = View.GONE + } + } else { + this.view?.setBackgroundColor(MainApp.gc(R.color.error_background)) + localprofile_profileswitch.visibility = View.GONE + localprofile_save.visibility = View.GONE //don't save an invalid profile + } + + //Show reset button if data was edited + if (isEdited) { + localprofile_reset.visibility = View.VISIBLE + } else { + localprofile_reset.visibility = View.GONE + } + } + + private fun processVisibilityOnClick(selected: View) { + dia_tab.setBackgroundColor(MainApp.gc(R.color.defaultbackground)) + ic_tab.setBackgroundColor(MainApp.gc(R.color.defaultbackground)) + isf_tab.setBackgroundColor(MainApp.gc(R.color.defaultbackground)) + basal_tab.setBackgroundColor(MainApp.gc(R.color.defaultbackground)) + target_tab.setBackgroundColor(MainApp.gc(R.color.defaultbackground)) + selected.setBackgroundColor(MainApp.gc(R.color.tabBgColorSelected)) + localprofile_dia_placeholder.visibility = View.GONE + localprofile_ic.visibility = View.GONE + localprofile_isf.visibility = View.GONE + localprofile_basal.visibility = View.GONE + localprofile_target.visibility = View.GONE + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.java deleted file mode 100644 index e3e068c1cb..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.java +++ /dev/null @@ -1,231 +0,0 @@ -package info.nightscout.androidaps.plugins.profile.local; - -import androidx.annotation.NonNull; - -import org.json.JSONArray; -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.ProfileStore; -import info.nightscout.androidaps.events.EventProfileStoreChanged; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.SP; - -/** - * Created by mike on 05.08.2016. - */ -public class LocalProfilePlugin extends PluginBase implements ProfileInterface { - public static final String LOCAL_PROFILE = "LocalProfile"; - private static Logger log = LoggerFactory.getLogger(L.PROFILE); - - private static LocalProfilePlugin localProfilePlugin; - - public static LocalProfilePlugin getPlugin() { - if (localProfilePlugin == null) - localProfilePlugin = new LocalProfilePlugin(); - return localProfilePlugin; - } - - private ProfileStore convertedProfile = null; - - private static final String DEFAULTARRAY = "[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":0}]"; - - public boolean isEdited() { - return edited; - } - - public void setEdited(boolean edited) { - this.edited = edited; - } - - boolean edited; - boolean mgdl; - boolean mmol; - Double dia; - JSONArray ic; - JSONArray isf; - JSONArray basal; - JSONArray targetLow; - JSONArray targetHigh; - - public LocalProfilePlugin() { - super(new PluginDescription() - .mainType(PluginType.PROFILE) - .fragmentClass(LocalProfileFragment.class.getName()) - .pluginName(R.string.localprofile) - .shortName(R.string.localprofile_shortname) - .description(R.string.description_profile_local) - ); - loadSettings(); - } - - public synchronized void storeSettings() { - SP.putBoolean(LOCAL_PROFILE + "mmol", mmol); - SP.putBoolean(LOCAL_PROFILE + "mgdl", mgdl); - SP.putString(LOCAL_PROFILE + "dia", dia.toString()); - SP.putString(LOCAL_PROFILE + "ic", ic.toString()); - SP.putString(LOCAL_PROFILE + "isf", isf.toString()); - SP.putString(LOCAL_PROFILE + "basal", basal.toString()); - SP.putString(LOCAL_PROFILE + "targetlow", targetLow.toString()); - SP.putString(LOCAL_PROFILE + "targethigh", targetHigh.toString()); - - createAndStoreConvertedProfile(); - edited = false; - if (L.isEnabled(L.PROFILE)) - log.debug("Storing settings: " + getRawProfile().getData().toString()); - RxBus.INSTANCE.send(new EventProfileStoreChanged()); - } - - public synchronized void loadSettings() { - if (L.isEnabled(L.PROFILE)) - log.debug("Loading stored settings"); - - mgdl = SP.getBoolean(LOCAL_PROFILE + "mgdl", false); - mmol = SP.getBoolean(LOCAL_PROFILE + "mmol", true); - dia = SP.getDouble(LOCAL_PROFILE + "dia", Constants.defaultDIA); - try { - ic = new JSONArray(SP.getString(LOCAL_PROFILE + "ic", DEFAULTARRAY)); - } catch (JSONException e1) { - try { - ic = new JSONArray(DEFAULTARRAY); - } catch (JSONException ignored) { - } - } - try { - isf = new JSONArray(SP.getString(LOCAL_PROFILE + "isf", DEFAULTARRAY)); - } catch (JSONException e1) { - try { - isf = new JSONArray(DEFAULTARRAY); - } catch (JSONException ignored) { - } - } - try { - basal = new JSONArray(SP.getString(LOCAL_PROFILE + "basal", DEFAULTARRAY)); - } catch (JSONException e1) { - try { - basal = new JSONArray(DEFAULTARRAY); - } catch (JSONException ignored) { - } - } - try { - targetLow = new JSONArray(SP.getString(LOCAL_PROFILE + "targetlow", DEFAULTARRAY)); - } catch (JSONException e1) { - try { - targetLow = new JSONArray(DEFAULTARRAY); - } catch (JSONException ignored) { - } - } - try { - targetHigh = new JSONArray(SP.getString(LOCAL_PROFILE + "targethigh", DEFAULTARRAY)); - } catch (JSONException e1) { - try { - targetHigh = new JSONArray(DEFAULTARRAY); - } catch (JSONException ignored) { - } - } - edited = false; - createAndStoreConvertedProfile(); - } - - /* - { - "_id": "576264a12771b7500d7ad184", - "startDate": "2016-06-16T08:35:00.000Z", - "defaultProfile": "Default", - "store": { - "Default": { - "dia": "3", - "carbratio": [{ - "time": "00:00", - "value": "30" - }], - "carbs_hr": "20", - "delay": "20", - "sens": [{ - "time": "00:00", - "value": "100" - }], - "timezone": "UTC", - "basal": [{ - "time": "00:00", - "value": "0.1" - }], - "target_low": [{ - "time": "00:00", - "value": "0" - }], - "target_high": [{ - "time": "00:00", - "value": "0" - }], - "startDate": "1970-01-01T00:00:00.000Z", - "units": "mmol" - } - }, - "created_at": "2016-06-16T08:34:41.256Z" - } - */ - private void createAndStoreConvertedProfile() { - convertedProfile = createProfileStore(); - } - - public synchronized boolean isValidEditState() { - return createProfileStore().getDefaultProfile().isValid(MainApp.gs(R.string.localprofile), false); - } - - @NonNull - public ProfileStore createProfileStore() { - JSONObject json = new JSONObject(); - JSONObject store = new JSONObject(); - JSONObject profile = new JSONObject(); - - try { - json.put("defaultProfile", LOCAL_PROFILE); - json.put("store", store); - profile.put("dia", dia); - profile.put("carbratio", ic); - profile.put("sens", isf); - profile.put("basal", basal); - profile.put("target_low", targetLow); - profile.put("target_high", targetHigh); - profile.put("units", mgdl ? Constants.MGDL : Constants.MMOL); - store.put(LOCAL_PROFILE, profile); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return new ProfileStore(json); - } - - @Override - public ProfileStore getProfile() { - if (!convertedProfile.getDefaultProfile().isValid(MainApp.gs(R.string.localprofile))) - return null; - return convertedProfile; - } - - public ProfileStore getRawProfile() { - return convertedProfile; - } - - @Override - public String getUnits() { - return mgdl ? Constants.MGDL : Constants.MMOL; - } - - @Override - public String getProfileName() { - return DecimalFormatter.to2Decimal(convertedProfile.getDefaultProfile().percentageBasalSum()) + "U "; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt new file mode 100644 index 0000000000..a3e3819b51 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt @@ -0,0 +1,429 @@ +package info.nightscout.androidaps.plugins.profile.local + +import android.app.Activity +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.ProfileStore +import info.nightscout.androidaps.events.EventProfileStoreChanged +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.ProfileInterface +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.SP +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import org.slf4j.LoggerFactory +import java.util.* +import kotlin.collections.ArrayList + +object LocalProfilePlugin : PluginBase(PluginDescription() + .mainType(PluginType.PROFILE) + .fragmentClass(LocalProfileFragment::class.java.name) + .pluginName(R.string.localprofile) + .shortName(R.string.localprofile_shortname) + .description(R.string.description_profile_local)), ProfileInterface { + + override fun onStart() { + super.onStart() + loadSettings() + } + + private val log = LoggerFactory.getLogger(L.PROFILE) + + private var rawProfile: ProfileStore? = null + + const val LOCAL_PROFILE = "LocalProfile" + + private const val DEFAULTARRAY = "[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":0}]" + + class SingleProfile { + internal var name: String? = null + internal var mgdl: Boolean = false + internal var dia: Double = Constants.defaultDIA + internal var ic: JSONArray? = null + internal var isf: JSONArray? = null + internal var basal: JSONArray? = null + internal var targetLow: JSONArray? = null + internal var targetHigh: JSONArray? = null + + fun deepClone(): SingleProfile { + val sp = SingleProfile() + sp.name = name + sp.mgdl = mgdl + sp.dia = dia + sp.ic = JSONArray(ic.toString()) + sp.isf = JSONArray(isf.toString()) + sp.basal = JSONArray(basal.toString()) + sp.targetLow = JSONArray(targetLow.toString()) + sp.targetHigh = JSONArray(targetHigh.toString()) + return sp + } + + fun copyFrom(profile: Profile, newName: String): SingleProfile { + var verifiedName = newName + if (rawProfile?.getSpecificProfile(newName) != null) { + verifiedName += " " + DateUtil.now().toString() + } + val sp = SingleProfile() + sp.name = verifiedName + sp.mgdl = profile.units == Constants.MGDL + sp.dia = profile.dia + sp.ic = JSONArray(profile.data.getJSONArray("carbratio").toString()) + sp.isf = JSONArray(profile.data.getJSONArray("sens").toString()) + sp.basal = JSONArray(profile.data.getJSONArray("basal").toString()) + sp.targetLow = JSONArray(profile.data.getJSONArray("target_low").toString()) + sp.targetHigh = JSONArray(profile.data.getJSONArray("target_high").toString()) + return sp + } + } + + var isEdited: Boolean = false + var profiles: ArrayList = ArrayList() + + internal var numOfProfiles = 0 + internal var currentProfileIndex = 0 + + fun currentProfile() = profiles[currentProfileIndex] + + @Synchronized + fun isValidEditState(): Boolean { + return createProfileStore().getDefaultProfile()?.isValid(MainApp.gs(R.string.localprofile), false) + ?: false + } + + @Synchronized + fun storeSettings(activity: Activity? = null) { + for (i in 0 until numOfProfiles) { + profiles[i].run { + val LOCAL_PROFILE_NUMBERED = LOCAL_PROFILE + "_" + i + "_" + SP.putString(LOCAL_PROFILE_NUMBERED + "name", name) + SP.putBoolean(LOCAL_PROFILE_NUMBERED + "mgdl", mgdl) + SP.putDouble(LOCAL_PROFILE_NUMBERED + "dia", dia) + SP.putString(LOCAL_PROFILE_NUMBERED + "ic", ic.toString()) + SP.putString(LOCAL_PROFILE_NUMBERED + "isf", isf.toString()) + SP.putString(LOCAL_PROFILE_NUMBERED + "basal", basal.toString()) + SP.putString(LOCAL_PROFILE_NUMBERED + "targetlow", targetLow.toString()) + SP.putString(LOCAL_PROFILE_NUMBERED + "targethigh", targetHigh.toString()) + } + } + SP.putInt(LOCAL_PROFILE + "_profiles", numOfProfiles) + + createAndStoreConvertedProfile() + isEdited = false + if (L.isEnabled(L.PROFILE)) + log.debug("Storing settings: " + rawProfile?.data.toString()) + RxBus.send(EventProfileStoreChanged()) + var namesOK = true + profiles.forEach { + val name = it.name ?: "." + if (name.contains(".")) namesOK = false + } + if (namesOK) + rawProfile?.let { NSUpload.uploadProfileStore(it.data) } + else + activity?.let { + OKDialog.show(it, "", MainApp.gs(R.string.profilenamecontainsdot)) + } + } + + @Synchronized + fun loadSettings() { + if (SP.contains(LOCAL_PROFILE + "mgdl")) { + doConversion() + return + } + + numOfProfiles = SP.getInt(LOCAL_PROFILE + "_profiles", 0) + profiles.clear() + numOfProfiles = Math.max(numOfProfiles, 1) // create at least one default profile if none exists + + for (i in 0 until numOfProfiles) { + val p = SingleProfile() + val LOCAL_PROFILE_NUMBERED = LOCAL_PROFILE + "_" + i + "_" + + p.name = SP.getString(LOCAL_PROFILE_NUMBERED + "name", LOCAL_PROFILE + i) + if (isExistingName(p.name)) continue + p.mgdl = SP.getBoolean(LOCAL_PROFILE_NUMBERED + "mgdl", false) + p.dia = SP.getDouble(LOCAL_PROFILE_NUMBERED + "dia", Constants.defaultDIA) + try { + p.ic = JSONArray(SP.getString(LOCAL_PROFILE_NUMBERED + "ic", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.ic = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + log.error("Exception", e1) + } + + try { + p.isf = JSONArray(SP.getString(LOCAL_PROFILE_NUMBERED + "isf", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.isf = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + log.error("Exception", e1) + } + + try { + p.basal = JSONArray(SP.getString(LOCAL_PROFILE_NUMBERED + "basal", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.basal = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + log.error("Exception", e1) + } + + try { + p.targetLow = JSONArray(SP.getString(LOCAL_PROFILE_NUMBERED + "targetlow", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.targetLow = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + log.error("Exception", e1) + } + + try { + p.targetHigh = JSONArray(SP.getString(LOCAL_PROFILE_NUMBERED + "targethigh", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.targetHigh = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + log.error("Exception", e1) + } + + profiles.add(p) + } + isEdited = false + numOfProfiles = profiles.size + createAndStoreConvertedProfile() + } + + private fun isExistingName(name: String?): Boolean { + for (p in profiles) { + if (p.name == name) return true + } + return false + } + + @Synchronized + private fun doConversion() { // conversion from 2.3 to 2.4 format + if (L.isEnabled(L.PROFILE)) + log.debug("Loading stored settings") + val p = SingleProfile() + + p.mgdl = SP.getBoolean(LOCAL_PROFILE + "mgdl", ProfileFunctions.getSystemUnits() == Constants.MGDL) + p.dia = SP.getDouble(LOCAL_PROFILE + "dia", Constants.defaultDIA) + try { + p.ic = JSONArray(SP.getString(LOCAL_PROFILE + "ic", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.ic = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + } + + try { + p.isf = JSONArray(SP.getString(LOCAL_PROFILE + "isf", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.isf = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + } + + try { + p.basal = JSONArray(SP.getString(LOCAL_PROFILE + "basal", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.basal = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + } + + try { + p.targetLow = JSONArray(SP.getString(LOCAL_PROFILE + "targetlow", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.targetLow = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + } + + try { + p.targetHigh = JSONArray(SP.getString(LOCAL_PROFILE + "targethigh", DEFAULTARRAY)) + } catch (e1: JSONException) { + try { + p.targetHigh = JSONArray(DEFAULTARRAY) + } catch (ignored: JSONException) { + } + } + p.name = LOCAL_PROFILE + + SP.remove(LOCAL_PROFILE + "mgdl") + SP.remove(LOCAL_PROFILE + "mmol") + SP.remove(LOCAL_PROFILE + "dia") + SP.remove(LOCAL_PROFILE + "ic") + SP.remove(LOCAL_PROFILE + "isf") + SP.remove(LOCAL_PROFILE + "basal") + SP.remove(LOCAL_PROFILE + "targetlow") + SP.remove(LOCAL_PROFILE + "targethigh") + + currentProfileIndex = 0 + numOfProfiles = 1 + profiles.clear() + profiles.add(p) + storeSettings() + + isEdited = false + createAndStoreConvertedProfile() + } + + /* + { + "_id": "576264a12771b7500d7ad184", + "startDate": "2016-06-16T08:35:00.000Z", + "defaultProfile": "Default", + "store": { + "Default": { + "dia": "3", + "carbratio": [{ + "time": "00:00", + "value": "30" + }], + "carbs_hr": "20", + "delay": "20", + "sens": [{ + "time": "00:00", + "value": "100" + }], + "timezone": "UTC", + "basal": [{ + "time": "00:00", + "value": "0.1" + }], + "target_low": [{ + "time": "00:00", + "value": "0" + }], + "target_high": [{ + "time": "00:00", + "value": "0" + }], + "startDate": "1970-01-01T00:00:00.000Z", + "units": "mmol" + } + }, + "created_at": "2016-06-16T08:34:41.256Z" + } + */ + private fun createAndStoreConvertedProfile() { + rawProfile = createProfileStore() + } + + fun addNewProfile() { + var free = 0 + for (i in 1..10000) { + if (rawProfile?.getSpecificProfile(LOCAL_PROFILE + i) == null) { + free = i; + break + } + } + val p = SingleProfile() + p.name = LOCAL_PROFILE + free + p.mgdl = ProfileFunctions.getSystemUnits() == Constants.MGDL + p.dia = Constants.defaultDIA + p.ic = JSONArray(DEFAULTARRAY) + p.isf = JSONArray(DEFAULTARRAY) + p.basal = JSONArray(DEFAULTARRAY) + p.targetLow = JSONArray(DEFAULTARRAY) + p.targetHigh = JSONArray(DEFAULTARRAY) + profiles.add(p) + currentProfileIndex = profiles.size - 1 + numOfProfiles++ + createAndStoreConvertedProfile() + storeSettings() + } + + fun cloneProfile() { + val p = profiles[currentProfileIndex].deepClone() + p.name = p.name + " copy" + profiles.add(p) + currentProfileIndex = profiles.size - 1 + numOfProfiles++ + createAndStoreConvertedProfile() + storeSettings() + isEdited = false + } + + fun addProfile(p: SingleProfile) { + profiles.add(p) + currentProfileIndex = profiles.size - 1 + numOfProfiles++ + createAndStoreConvertedProfile() + storeSettings() + isEdited = false + } + + fun removeCurrentProfile() { + profiles.removeAt(currentProfileIndex) + numOfProfiles-- + if (profiles.size == 0) addNewProfile() + currentProfileIndex = 0 + createAndStoreConvertedProfile() + storeSettings() + isEdited = false + } + + fun createProfileStore(): ProfileStore { + val json = JSONObject() + val store = JSONObject() + + try { + for (i in 0 until numOfProfiles) { + profiles[i].run { + val profile = JSONObject() + profile.put("dia", dia) + profile.put("carbratio", ic) + profile.put("sens", isf) + profile.put("basal", basal) + profile.put("target_low", targetLow) + profile.put("target_high", targetHigh) + profile.put("units", if (mgdl) Constants.MGDL else Constants.MMOL) + profile.put("timezone", TimeZone.getDefault().id) + store.put(name, profile) + } + } + json.put("defaultProfile", currentProfile().name) + json.put("startDate", DateUtil.toISOAsUTC(DateUtil.now())) + json.put("store", store) + } catch (e: JSONException) { + log.error("Unhandled exception", e) + } + + return ProfileStore(json) + } + + override fun getProfile(): ProfileStore? { + return rawProfile + } + + override fun getProfileName(): String { + return DecimalFormatter.to2Decimal(rawProfile?.getDefaultProfile()?.percentageBasalSum() + ?: 0.0) + "U " + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/events/EventLocalProfileChanged.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/events/EventLocalProfileChanged.kt new file mode 100644 index 0000000000..c84eaecd7f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/events/EventLocalProfileChanged.kt @@ -0,0 +1,6 @@ +package info.nightscout.androidaps.plugins.profile.local.events + +import info.nightscout.androidaps.events.Event + +class EventLocalProfileChanged : Event() { +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt index 7a76283074..6c111faf3f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt @@ -12,6 +12,7 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions import info.nightscout.androidaps.plugins.profile.ns.events.EventNSProfileUpdateGUI +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.OKDialog @@ -21,7 +22,6 @@ import kotlinx.android.synthetic.main.close.* import kotlinx.android.synthetic.main.nsprofile_fragment.* import kotlinx.android.synthetic.main.profileviewer_fragment.* - class NSProfileFragment : Fragment() { private var disposable: CompositeDisposable = CompositeDisposable() @@ -39,10 +39,11 @@ class NSProfileFragment : Fragment() { val name = nsprofile_spinner.selectedItem?.toString() ?: "" NSProfilePlugin.getPlugin().profile?.let { store -> store.getSpecificProfile(name)?.let { - OKDialog.showConfirmation(activity, - MainApp.gs(R.string.activate_profile) + ": " + name + " ?" - ) { - ProfileFunctions.doProfileSwitch(store, name, 0, 100, 0) + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.nsprofile), + MainApp.gs(R.string.activate_profile) + ": " + name + " ?", Runnable { + ProfileFunctions.doProfileSwitch(store, name, 0, 100, 0, DateUtil.now()) + }) } } } @@ -96,13 +97,9 @@ class NSProfileFragment : Fragment() { override fun onResume() { super.onResume() disposable.add(RxBus - .toObservable(EventNSProfileUpdateGUI::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateGUI() - }, { - FabricPrivacy.logException(it) - }) + .toObservable(EventNSProfileUpdateGUI::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) ) updateGUI() } @@ -119,7 +116,7 @@ class NSProfileFragment : Fragment() { profileview_noprofile.visibility = View.VISIBLE NSProfilePlugin.getPlugin().profile?.let { profileStore -> - val profileList = profileStore.profileList + val profileList = profileStore.getProfileList() val adapter = ArrayAdapter(context!!, R.layout.spinner_centered, profileList) nsprofile_spinner.adapter = adapter // set selected to actual profile diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.java index 38afe62bdf..41711b47bd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.java @@ -118,11 +118,6 @@ public class NSProfilePlugin extends PluginBase implements ProfileInterface { return profile; } - @Override - public String getUnits() { - return profile != null ? profile.getUnits() : Constants.MGDL; - } - @Override public String getProfileName() { return profile.getDefaultProfileName(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/simple/SimpleProfileFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/profile/simple/SimpleProfileFragment.java deleted file mode 100644 index ecf4fc423b..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/simple/SimpleProfileFragment.java +++ /dev/null @@ -1,159 +0,0 @@ -package info.nightscout.androidaps.plugins.profile.simple; - - -import android.os.Bundle; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.RadioButton; -import android.widget.TextView; - -import androidx.fragment.app.Fragment; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventInitializationChanged; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment; -import info.nightscout.androidaps.plugins.general.careportal.Dialogs.NewNSTreatmentDialog; -import info.nightscout.androidaps.plugins.general.careportal.OptionsToShow; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.SafeParse; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -public class SimpleProfileFragment extends Fragment { - private CompositeDisposable disposable = new CompositeDisposable(); - - EditText diaView; - RadioButton mgdlView; - RadioButton mmolView; - EditText icView; - EditText isfView; - EditText basalView; - EditText targetlowView; - EditText targethighView; - Button profileswitchButton; - TextView invalidProfile; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.simpleprofile_fragment, container, false); - diaView = (EditText) layout.findViewById(R.id.simpleprofile_dia); - mgdlView = (RadioButton) layout.findViewById(R.id.simpleprofile_mgdl); - mmolView = (RadioButton) layout.findViewById(R.id.simpleprofile_mmol); - icView = (EditText) layout.findViewById(R.id.simpleprofile_ic); - isfView = (EditText) layout.findViewById(R.id.simpleprofile_isf); - basalView = (EditText) layout.findViewById(R.id.simpleprofile_basalrate); - targetlowView = (EditText) layout.findViewById(R.id.simpleprofile_targetlow); - targethighView = (EditText) layout.findViewById(R.id.simpleprofile_targethigh); - profileswitchButton = (Button) layout.findViewById(R.id.simpleprofile_profileswitch); - invalidProfile = (TextView) layout.findViewById(R.id.invalidprofile); - - if (!ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().isTempBasalCapable) { - layout.findViewById(R.id.simpleprofile_basalrate).setVisibility(View.GONE); - layout.findViewById(R.id.simpleprofile_basalrate_label).setVisibility(View.GONE); - } - - mgdlView.setChecked(SimpleProfilePlugin.getPlugin().mgdl); - mmolView.setChecked(SimpleProfilePlugin.getPlugin().mmol); - diaView.setText(SimpleProfilePlugin.getPlugin().dia.toString()); - icView.setText(SimpleProfilePlugin.getPlugin().ic.toString()); - isfView.setText(SimpleProfilePlugin.getPlugin().isf.toString()); - basalView.setText(SimpleProfilePlugin.getPlugin().basal.toString()); - targetlowView.setText(SimpleProfilePlugin.getPlugin().targetLow.toString()); - targethighView.setText(SimpleProfilePlugin.getPlugin().targetHigh.toString()); - - mgdlView.setOnClickListener(v -> { - SimpleProfilePlugin.getPlugin().mgdl = mgdlView.isChecked(); - SimpleProfilePlugin.getPlugin().mmol = !SimpleProfilePlugin.getPlugin().mgdl; - mmolView.setChecked(SimpleProfilePlugin.getPlugin().mmol); - SimpleProfilePlugin.getPlugin().storeSettings(); - }); - mmolView.setOnClickListener(v -> { - SimpleProfilePlugin.getPlugin().mmol = mmolView.isChecked(); - SimpleProfilePlugin.getPlugin().mgdl = !SimpleProfilePlugin.getPlugin().mmol; - mgdlView.setChecked(SimpleProfilePlugin.getPlugin().mgdl); - SimpleProfilePlugin.getPlugin().storeSettings(); - }); - - profileswitchButton.setOnClickListener(view -> { - NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); - final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCH; - profileswitch.executeProfileSwitch = true; - newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); - newDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); - }); - - TextWatcher textWatch = 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) { - SimpleProfilePlugin.getPlugin().dia = SafeParse.stringToDouble(diaView.getText().toString()); - SimpleProfilePlugin.getPlugin().ic = SafeParse.stringToDouble(icView.getText().toString()); - SimpleProfilePlugin.getPlugin().isf = SafeParse.stringToDouble(isfView.getText().toString()); - SimpleProfilePlugin.getPlugin().basal = SafeParse.stringToDouble(basalView.getText().toString()); - SimpleProfilePlugin.getPlugin().targetLow = SafeParse.stringToDouble(targetlowView.getText().toString()); - SimpleProfilePlugin.getPlugin().targetHigh = SafeParse.stringToDouble(targethighView.getText().toString()); - SimpleProfilePlugin.getPlugin().storeSettings(); - updateGUI(); - } - }; - - diaView.addTextChangedListener(textWatch); - icView.addTextChangedListener(textWatch); - isfView.addTextChangedListener(textWatch); - basalView.addTextChangedListener(textWatch); - targetlowView.addTextChangedListener(textWatch); - targethighView.addTextChangedListener(textWatch); - - return layout; - } - - @Override - public synchronized void onResume() { - super.onResume(); - disposable.add(RxBus.INSTANCE - .toObservable(EventInitializationChanged.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> updateGUI(), FabricPrivacy::logException) - ); - updateGUI(); - } - - @Override - public synchronized void onPause() { - super.onPause(); - disposable.clear(); - } - - protected void updateGUI() { - boolean isValid = SimpleProfilePlugin.getPlugin().getProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid(MainApp.gs(R.string.simpleprofile)); - if (!ConfigBuilderPlugin.getPlugin().getActivePump().isInitialized() || ConfigBuilderPlugin.getPlugin().getActivePump().isSuspended() || !isValid) { - profileswitchButton.setVisibility(View.GONE); - } else { - profileswitchButton.setVisibility(View.VISIBLE); - } - if (isValid) - invalidProfile.setVisibility(View.GONE); - else - invalidProfile.setVisibility(View.VISIBLE); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/simple/SimpleProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/profile/simple/SimpleProfilePlugin.java deleted file mode 100644 index 61ca403f55..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/simple/SimpleProfilePlugin.java +++ /dev/null @@ -1,181 +0,0 @@ -package info.nightscout.androidaps.plugins.profile.simple; - -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -import org.json.JSONArray; -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.ProfileStore; -import info.nightscout.androidaps.events.EventProfileStoreChanged; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.utils.SP; - -/** - * Created by mike on 05.08.2016. - */ -public class SimpleProfilePlugin extends PluginBase implements ProfileInterface { - private static Logger log = LoggerFactory.getLogger(L.PROFILE); - - private static SimpleProfilePlugin simpleProfilePlugin; - - public static SimpleProfilePlugin getPlugin() { - if (simpleProfilePlugin == null) - simpleProfilePlugin = new SimpleProfilePlugin(); - return simpleProfilePlugin; - } - - private static ProfileStore convertedProfile = null; - - boolean mgdl; - boolean mmol; - Double dia; - Double ic; - Double isf; - Double basal; - Double targetLow; - Double targetHigh; - - private SimpleProfilePlugin() { - super(new PluginDescription() - .mainType(PluginType.PROFILE) - .fragmentClass(SimpleProfileFragment.class.getName()) - .pluginName(R.string.simpleprofile) - .shortName(R.string.simpleprofile_shortname) - .description(R.string.description_profile_simple) - ); - loadSettings(); - } - - public void storeSettings() { - if (L.isEnabled(L.PROFILE)) - log.debug("Storing settings"); - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); - SharedPreferences.Editor editor = settings.edit(); - editor.putBoolean("SimpleProfile" + "mmol", mmol); - editor.putBoolean("SimpleProfile" + "mgdl", mgdl); - editor.putString("SimpleProfile" + "dia", dia.toString()); - editor.putString("SimpleProfile" + "ic", ic.toString()); - editor.putString("SimpleProfile" + "isf", isf.toString()); - editor.putString("SimpleProfile" + "basal", basal.toString()); - editor.putString("SimpleProfile" + "targetlow", targetLow.toString()); - editor.putString("SimpleProfile" + "targethigh", targetHigh.toString()); - - editor.apply(); - createConvertedProfile(); - if (L.isEnabled(L.PROFILE)) - log.debug("Storing settings: " + getRawProfile().getData().toString()); - RxBus.INSTANCE.send(new EventProfileStoreChanged()); - } - - private void loadSettings() { - if (L.isEnabled(L.PROFILE)) - log.debug("Loading stored settings"); - - mgdl = SP.getBoolean("SimpleProfile" + "mgdl", true); - mmol = SP.getBoolean("SimpleProfile" + "mmol", false); - dia = SP.getDouble("SimpleProfile" + "dia", Constants.defaultDIA); - ic = SP.getDouble("SimpleProfile" + "ic", 0d); - isf = SP.getDouble("SimpleProfile" + "isf", 0d); - basal = SP.getDouble("SimpleProfile" + "basal", 0d); - targetLow = SP.getDouble("SimpleProfile" + "targetlow", 0d); - targetHigh = SP.getDouble("SimpleProfile" + "targethigh", 0d); - } - - /* - { - "_id": "576264a12771b7500d7ad184", - "startDate": "2016-06-16T08:35:00.000Z", - "defaultProfile": "Default", - "store": { - "Default": { - "dia": "3", - "carbratio": [{ - "time": "00:00", - "value": "30" - }], - "carbs_hr": "20", - "delay": "20", - "sens": [{ - "time": "00:00", - "value": "100" - }], - "timezone": "UTC", - "basal": [{ - "time": "00:00", - "value": "0.1" - }], - "target_low": [{ - "time": "00:00", - "value": "0" - }], - "target_high": [{ - "time": "00:00", - "value": "0" - }], - "startDate": "1970-01-01T00:00:00.000Z", - "units": "mmol" - } - }, - "created_at": "2016-06-16T08:34:41.256Z" - } - */ - private void createConvertedProfile() { - JSONObject json = new JSONObject(); - JSONObject store = new JSONObject(); - JSONObject profile = new JSONObject(); - - try { - json.put("defaultProfile", "SimpleProfile"); - json.put("store", store); - profile.put("dia", dia); - profile.put("carbratio", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", ic))); - profile.put("sens", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", isf))); - profile.put("basal", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", basal))); - profile.put("target_low", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", targetLow))); - profile.put("target_high", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", targetHigh))); - profile.put("units", mgdl ? Constants.MGDL : Constants.MMOL); - store.put("SimpleProfile", profile); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - convertedProfile = new ProfileStore(json); - } - - @Override - public ProfileStore getProfile() { - if (convertedProfile == null) - createConvertedProfile(); - if (!convertedProfile.getDefaultProfile().isValid(MainApp.gs(R.string.simpleprofile))) - return null; - return convertedProfile; - } - - public ProfileStore getRawProfile() { - if (convertedProfile == null) - createConvertedProfile(); - return convertedProfile; - } - - @Override - public String getUnits() { - return mgdl ? Constants.MGDL : Constants.MMOL; - } - - @Override - public String getProfileName() { - return "SimpleProfile"; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java index 3e551b6ccf..a7b005b9b8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java @@ -16,6 +16,7 @@ import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; */ public class Encoding4b6bLoop extends Encoding4b6bAbstract { + private static final Logger log = LoggerFactory.getLogger(Encoding4b6bLoop.class); public static final Logger LOG = LoggerFactory.getLogger(Encoding4b6bLoop.class); public Map codesRev = null; @@ -108,9 +109,8 @@ public class Encoding4b6bLoop extends Encoding4b6bAbstract { int index2 = ((bitAccumulator >> (availBits - 12)) & 0b111111); hiNibble = codesRev.get((bitAccumulator >> (availBits - 6))); loNibble = codesRev.get(((bitAccumulator >> (availBits - 12)) & 0b111111)); - } catch (Exception ex) { - System.out.println("Exception: " + ex.getMessage()); - ex.printStackTrace(); + } catch (Exception e) { + log.error("Unhandled exception", e); return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java index 98682679a7..065f0ec4d6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java @@ -1,14 +1,13 @@ package info.nightscout.androidaps.plugins.pump.common.utils; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.location.LocationManager; -import android.os.Build; +import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.utils.OKDialog; /** * Helper for checking if location services are enabled on the device. @@ -45,18 +44,9 @@ public class LocationHelper { } // Shamelessly borrowed from http://stackoverflow.com/a/10311877/868533 - - AlertDialog.Builder builder = new AlertDialog.Builder(parent); - builder.setTitle(R.string.location_not_found_title); - builder.setMessage(R.string.location_not_found_message); - builder.setPositiveButton(R.string.location_yes, new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialogInterface, int i) { - parent.startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); - } + OKDialog.showConfirmation(parent, MainApp.gs(R.string.location_not_found_title), MainApp.gs(R.string.location_not_found_message), () -> { + parent.startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); }); - builder.setNegativeButton(R.string.location_no, null); - builder.create().show(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/AbstractDanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/AbstractDanaRPlugin.java index 1a40e449e5..57dadbdc55 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/AbstractDanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/AbstractDanaRPlugin.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.pump.danaR; -import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import org.json.JSONException; @@ -15,7 +14,6 @@ import info.nightscout.androidaps.BuildConfig; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.TemporaryBasal; @@ -25,7 +23,6 @@ import info.nightscout.androidaps.interfaces.DanaRInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; @@ -49,7 +46,7 @@ import info.nightscout.androidaps.utils.SP; * Created by mike on 28.01.2018. */ -public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInterface, DanaRInterface, ConstraintsInterface, ProfileInterface { +public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInterface, DanaRInterface, ConstraintsInterface { protected Logger log = LoggerFactory.getLogger(L.PUMP); protected AbstractDanaRExecutionService sExecutionService; @@ -143,7 +140,6 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte for (int h = 0; h < basalValues; h++) { Double pumpValue = pump.pumpProfiles[pump.activeProfile][h]; Double profileValue = profile.getBasalTimeFromMidnight(h * basalIncrement); - if (profileValue == null) return true; if (Math.abs(pumpValue - profileValue) > getPumpDescription().basalStep) { if (L.isEnabled(L.PUMP)) log.debug("Diff found. Hour: " + h + " Pump: " + pumpValue + " Profile: " + profileValue); @@ -436,24 +432,6 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte return applyBolusConstraints(insulin); } - @Nullable - @Override - public ProfileStore getProfile() { - if (DanaRPump.getInstance().lastSettingsRead == 0) - return null; // no info now - return DanaRPump.getInstance().createConvertedProfile(); - } - - @Override - public String getUnits() { - return DanaRPump.getInstance().getUnits(); - } - - @Override - public String getProfileName() { - return DanaRPump.getInstance().createConvertedProfileName(); - } - @Override public PumpEnactResult loadTDDs() { return loadHistory(RecordTypes.RECORD_TYPE_DAILY); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt index 3f58acf4d2..af1e80602e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.pump.danaR - import android.content.Intent import android.os.Bundle import android.os.Handler @@ -11,6 +10,7 @@ import androidx.fragment.app.Fragment import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.TDDStatsActivity +import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.events.EventExtendedBolusChange import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.events.EventTempBasalChange @@ -24,9 +24,12 @@ import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRUserOptions import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRNewStatus import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin -import info.nightscout.androidaps.plugins.treatments.fragments.ProfileViewerDialog import info.nightscout.androidaps.queue.events.EventQueueChanged -import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.SetWarnColor +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.plusAssign import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.danar_fragment.* @@ -59,9 +62,16 @@ class DanaRFragment : Fragment() { danar_history.setOnClickListener { startActivity(Intent(context, DanaRHistoryActivity::class.java)) } danar_viewprofile.setOnClickListener { fragmentManager?.let { fragmentManager -> + val profile = DanaRPump.getInstance().createConvertedProfile()?.getDefaultProfile() + ?: return@let + val profileName = DanaRPump.getInstance().createConvertedProfile()?.getDefaultProfileName() + ?: return@let val args = Bundle() args.putLong("time", DateUtil.now()) - args.putInt("mode", ProfileViewerDialog.Mode.PUMP_PROFILE.ordinal) + args.putInt("mode", ProfileViewerDialog.Mode.CUSTOM_PROFILE.ordinal) + args.putString("customProfile", profile.data.toString()) + args.putString("customProfileUnits", profile.units) + args.putString("customProfileName", profileName) val pvd = ProfileViewerDialog() pvd.arguments = args pvd.show(fragmentManager, "ProfileViewDialog") @@ -82,37 +92,37 @@ class DanaRFragment : Fragment() { super.onResume() loopHandler.postDelayed(refreshLoop, T.mins(1).msecs()) disposable += RxBus - .toObservable(EventDanaRNewStatus::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + .toObservable(EventDanaRNewStatus::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) disposable += RxBus - .toObservable(EventExtendedBolusChange::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + .toObservable(EventExtendedBolusChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) disposable += RxBus - .toObservable(EventTempBasalChange::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + .toObservable(EventTempBasalChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) disposable += RxBus - .toObservable(EventQueueChanged::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + .toObservable(EventQueueChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) disposable += RxBus - .toObservable(EventPumpStatusChanged::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - when { - it.sStatus == EventPumpStatusChanged.Status.CONNECTING -> danar_btconnection?.text = "{fa-bluetooth-b spin} " + it.sSecondsElapsed + "s" - it.sStatus == EventPumpStatusChanged.Status.CONNECTED -> danar_btconnection?.text = "{fa-bluetooth}" - it.sStatus == EventPumpStatusChanged.Status.DISCONNECTED -> danar_btconnection?.text = "{fa-bluetooth-b}" - } - if (it.getStatus() != "") { - dana_pumpstatus?.text = it.getStatus() - dana_pumpstatuslayout?.visibility = View.VISIBLE - } else { - dana_pumpstatuslayout?.visibility = View.GONE - } - }, { FabricPrivacy.logException(it) }) + .toObservable(EventPumpStatusChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + when { + it.sStatus == EventPumpStatusChanged.Status.CONNECTING -> danar_btconnection?.text = "{fa-bluetooth-b spin} " + it.sSecondsElapsed + "s" + it.sStatus == EventPumpStatusChanged.Status.CONNECTED -> danar_btconnection?.text = "{fa-bluetooth}" + it.sStatus == EventPumpStatusChanged.Status.DISCONNECTED -> danar_btconnection?.text = "{fa-bluetooth-b}" + } + if (it.getStatus() != "") { + dana_pumpstatus?.text = it.getStatus() + dana_pumpstatuslayout?.visibility = View.VISIBLE + } else { + dana_pumpstatuslayout?.visibility = View.GONE + } + }, { FabricPrivacy.logException(it) }) updateGUI() } @@ -151,14 +161,14 @@ class DanaRFragment : Fragment() { // DanaRPlugin, DanaRKoreanPlugin if (ConfigBuilderPlugin.getPlugin().activePump!!.isFakingTempsByExtendedBoluses) { danar_tempbasal.text = TreatmentsPlugin.getPlugin() - .getRealTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() ?: "" + .getRealTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() ?: "" } else { // v2 plugin danar_tempbasal.text = TreatmentsPlugin.getPlugin() - .getTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() ?: "" + .getTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() ?: "" } danar_extendedbolus.text = TreatmentsPlugin.getPlugin() - .getExtendedBolusFromHistory(System.currentTimeMillis())?.toString() ?: "" + .getExtendedBolusFromHistory(System.currentTimeMillis())?.toString() ?: "" danar_reservoir.text = MainApp.gs(R.string.reservoirvalue, pump.reservoirRemainingUnits, 300) SetWarnColor.setColorInverse(danar_reservoir, pump.reservoirRemainingUnits, 50.0, 20.0) danar_battery.text = "{fa-battery-" + pump.batteryRemaining / 25 + "}" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.java deleted file mode 100644 index f20a74b024..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.java +++ /dev/null @@ -1,335 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaR.activities; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.Spinner; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.cardview.widget.CardView; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.activities.NoSplashActivity; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.DanaRHistoryRecord; -import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.pump.danaR.comm.RecordTypes; -import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRSyncStatus; -import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin; -import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.ToastUtils; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -public class DanaRHistoryActivity extends NoSplashActivity { - private static Logger log = LoggerFactory.getLogger(L.PUMP); - private CompositeDisposable disposable = new CompositeDisposable(); - - static Profile profile = null; - - Spinner historyTypeSpinner; - TextView statusView; - Button reloadButton; - RecyclerView recyclerView; - LinearLayoutManager llm; - - static byte showingType = RecordTypes.RECORD_TYPE_ALARM; - List historyList = new ArrayList<>(); - - public static class TypeList { - public byte type; - String name; - - TypeList(byte type, String name) { - this.type = type; - this.name = name; - } - - @NonNull - @Override - public String toString() { - return name; - } - } - - public DanaRHistoryActivity() { - super(); - } - - - @Override - protected void onResume() { - super.onResume(); - disposable.add(RxBus.INSTANCE - .toObservable(EventPumpStatusChanged.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> statusView.setText(event.getStatus()), FabricPrivacy::logException) - ); - disposable.add(RxBus.INSTANCE - .toObservable(EventDanaRSyncStatus.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> { - if (L.isEnabled(L.PUMP)) - log.debug("EventDanaRSyncStatus: " + event.getMessage()); - statusView.setText(event.getMessage()); - }, FabricPrivacy::logException) - ); - } - - @Override - protected void onPause() { - super.onPause(); - disposable.clear(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.danar_historyactivity); - - historyTypeSpinner = findViewById(R.id.danar_historytype); - statusView = findViewById(R.id.danar_historystatus); - reloadButton = findViewById(R.id.danar_historyreload); - recyclerView = findViewById(R.id.danar_history_recyclerview); - - recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(this); - recyclerView.setLayoutManager(llm); - - RecyclerViewAdapter adapter = new RecyclerViewAdapter(historyList); - recyclerView.setAdapter(adapter); - - statusView.setVisibility(View.GONE); - - boolean isKorean = DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PUMP); - boolean isRS = DanaRSPlugin.getPlugin().isEnabled(PluginType.PUMP); - - // Types - - ArrayList typeList = new ArrayList<>(); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_ALARM, MainApp.gs(R.string.danar_history_alarm))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_BASALHOUR, MainApp.gs(R.string.danar_history_basalhours))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_BOLUS, MainApp.gs(R.string.danar_history_bolus))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_CARBO, MainApp.gs(R.string.danar_history_carbohydrates))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_DAILY, MainApp.gs(R.string.danar_history_dailyinsulin))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_GLUCOSE, MainApp.gs(R.string.danar_history_glucose))); - if (!isKorean && !isRS) { - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_ERROR, MainApp.gs(R.string.danar_history_errors))); - } - if (isRS) - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_PRIME, MainApp.gs(R.string.danar_history_prime))); - if (!isKorean) { - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_REFILL, MainApp.gs(R.string.danar_history_refill))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_SUSPEND, MainApp.gs(R.string.danar_history_syspend))); - } - ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, - R.layout.spinner_centered, typeList); - historyTypeSpinner.setAdapter(spinnerAdapter); - - reloadButton.setOnClickListener(v -> { - final TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); - runOnUiThread(() -> { - reloadButton.setVisibility(View.GONE); - statusView.setVisibility(View.VISIBLE); - }); - clearCardView(); - ConfigBuilderPlugin.getPlugin().getCommandQueue().loadHistory(selected.type, new Callback() { - @Override - public void run() { - loadDataFromDB(selected.type); - runOnUiThread(() -> { - reloadButton.setVisibility(View.VISIBLE); - statusView.setVisibility(View.GONE); - }); - } - }); - }); - - historyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); - loadDataFromDB(selected.type); - showingType = selected.type; - } - - @Override - public void onNothingSelected(AdapterView parent) { - clearCardView(); - } - }); - profile = ProfileFunctions.getInstance().getProfile(); - if (profile == null) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.noprofile)); - finish(); - } - } - - public static class RecyclerViewAdapter extends RecyclerView.Adapter { - - List historyList; - - RecyclerViewAdapter(List historyList) { - this.historyList = historyList; - } - - @NonNull - @Override - public HistoryViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.danar_history_item, viewGroup, false); - return new HistoryViewHolder(v); - } - - @Override - public void onBindViewHolder(@NonNull HistoryViewHolder holder, int position) { - DanaRHistoryRecord record = historyList.get(position); - holder.time.setText(DateUtil.dateAndTimeString(record.recordDate)); - holder.value.setText(DecimalFormatter.to2Decimal(record.recordValue)); - holder.stringvalue.setText(record.stringRecordValue); - holder.bolustype.setText(record.bolusType); - holder.duration.setText(DecimalFormatter.to0Decimal(record.recordDuration)); - holder.alarm.setText(record.recordAlarm); - switch (showingType) { - case RecordTypes.RECORD_TYPE_ALARM: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.VISIBLE); - break; - case RecordTypes.RECORD_TYPE_BOLUS: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.VISIBLE); - holder.duration.setVisibility(View.VISIBLE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_DAILY: - holder.dailybasal.setText(DecimalFormatter.to2Decimal(record.recordDailyBasal) + "U"); - holder.dailybolus.setText(DecimalFormatter.to2Decimal(record.recordDailyBolus) + "U"); - holder.dailytotal.setText(DecimalFormatter.to2Decimal(record.recordDailyBolus + record.recordDailyBasal) + "U"); - holder.time.setText(DateUtil.dateString(record.recordDate)); - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.GONE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.VISIBLE); - holder.dailybolus.setVisibility(View.VISIBLE); - holder.dailytotal.setVisibility(View.VISIBLE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_GLUCOSE: - holder.value.setText(Profile.toUnitsString(record.recordValue, record.recordValue * Constants.MGDL_TO_MMOLL, profile.getUnits())); - // rest is the same - case RecordTypes.RECORD_TYPE_CARBO: - case RecordTypes.RECORD_TYPE_BASALHOUR: - case RecordTypes.RECORD_TYPE_ERROR: - case RecordTypes.RECORD_TYPE_PRIME: - case RecordTypes.RECORD_TYPE_REFILL: - case RecordTypes.RECORD_TYPE_TB: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_SUSPEND: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.GONE); - holder.stringvalue.setVisibility(View.VISIBLE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - } - } - - @Override - public int getItemCount() { - return historyList.size(); - } - - @Override - public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - } - - static class HistoryViewHolder extends RecyclerView.ViewHolder { - CardView cv; - TextView time; - TextView value; - TextView bolustype; - TextView stringvalue; - TextView duration; - TextView dailybasal; - TextView dailybolus; - TextView dailytotal; - TextView alarm; - - HistoryViewHolder(View itemView) { - super(itemView); - cv = itemView.findViewById(R.id.danar_history_cardview); - time = itemView.findViewById(R.id.danar_history_time); - value = itemView.findViewById(R.id.danar_history_value); - bolustype = itemView.findViewById(R.id.danar_history_bolustype); - stringvalue = itemView.findViewById(R.id.danar_history_stringvalue); - duration = itemView.findViewById(R.id.danar_history_duration); - dailybasal = itemView.findViewById(R.id.danar_history_dailybasal); - dailybolus = itemView.findViewById(R.id.danar_history_dailybolus); - dailytotal = itemView.findViewById(R.id.danar_history_dailytotal); - alarm = itemView.findViewById(R.id.danar_history_alarm); - } - } - } - - private void loadDataFromDB(byte type) { - historyList = MainApp.getDbHelper().getDanaRHistoryRecordsByType(type); - - runOnUiThread(() -> recyclerView.swapAdapter(new RecyclerViewAdapter(historyList), false)); - } - - private void clearCardView() { - historyList = new ArrayList<>(); - runOnUiThread(() -> recyclerView.swapAdapter(new RecyclerViewAdapter(historyList), false)); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.kt new file mode 100644 index 0000000000..d63ab0eb14 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.kt @@ -0,0 +1,248 @@ +package info.nightscout.androidaps.plugins.pump.danaR.activities + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.DanaRHistoryRecord +import info.nightscout.androidaps.events.EventPumpStatusChanged +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus.toObservable +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.pump.danaR.comm.RecordTypes +import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRSyncStatus +import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin +import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.FabricPrivacy +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.danar_historyactivity.* +import org.slf4j.LoggerFactory +import java.util.* + +class DanaRHistoryActivity : NoSplashAppCompatActivity() { + private val log = LoggerFactory.getLogger(L.PUMP) + private val disposable = CompositeDisposable() + + private var showingType = RecordTypes.RECORD_TYPE_ALARM + private var historyList: List = ArrayList() + + class TypeList internal constructor(var type: Byte, var name: String) { + override fun toString(): String = name + } + + override fun onResume() { + super.onResume() + disposable.add(toObservable(EventPumpStatusChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ danar_history_status.text = it.getStatus() }) { FabricPrivacy.logException(it) } + ) + disposable.add(toObservable(EventDanaRSyncStatus::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + if (L.isEnabled(L.PUMP)) + log.debug("EventDanaRSyncStatus: " + it.message) + danar_history_status.text = it.message + }) { FabricPrivacy.logException(it) } + ) + } + + override fun onPause() { + super.onPause() + disposable.clear() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.danar_historyactivity) + + danar_history_recyclerview.setHasFixedSize(true) + danar_history_recyclerview.layoutManager = LinearLayoutManager(this) + danar_history_recyclerview.adapter = RecyclerViewAdapter(historyList) + danar_history_status.visibility = View.GONE + + val isKorean = DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PUMP) + val isRS = DanaRSPlugin.getPlugin().isEnabled(PluginType.PUMP) + + // Types + val typeList = ArrayList() + typeList.add(TypeList(RecordTypes.RECORD_TYPE_ALARM, MainApp.gs(R.string.danar_history_alarm))) + typeList.add(TypeList(RecordTypes.RECORD_TYPE_BASALHOUR, MainApp.gs(R.string.danar_history_basalhours))) + typeList.add(TypeList(RecordTypes.RECORD_TYPE_BOLUS, MainApp.gs(R.string.danar_history_bolus))) + typeList.add(TypeList(RecordTypes.RECORD_TYPE_CARBO, MainApp.gs(R.string.danar_history_carbohydrates))) + typeList.add(TypeList(RecordTypes.RECORD_TYPE_DAILY, MainApp.gs(R.string.danar_history_dailyinsulin))) + typeList.add(TypeList(RecordTypes.RECORD_TYPE_GLUCOSE, MainApp.gs(R.string.danar_history_glucose))) + if (!isKorean && !isRS) { + typeList.add(TypeList(RecordTypes.RECORD_TYPE_ERROR, MainApp.gs(R.string.danar_history_errors))) + } + if (isRS) typeList.add(TypeList(RecordTypes.RECORD_TYPE_PRIME, MainApp.gs(R.string.danar_history_prime))) + if (!isKorean) { + typeList.add(TypeList(RecordTypes.RECORD_TYPE_REFILL, MainApp.gs(R.string.danar_history_refill))) + typeList.add(TypeList(RecordTypes.RECORD_TYPE_SUSPEND, MainApp.gs(R.string.danar_history_syspend))) + } + danar_history_spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, typeList) + + danar_history_reload.setOnClickListener { + val selected = danar_history_spinner.selectedItem as TypeList + runOnUiThread { + danar_history_reload?.visibility = View.GONE + danar_history_status?.visibility = View.VISIBLE + } + clearCardView() + ConfigBuilderPlugin.getPlugin().commandQueue.loadHistory(selected.type, object : Callback() { + override fun run() { + loadDataFromDB(selected.type) + runOnUiThread { + danar_history_reload?.visibility = View.VISIBLE + danar_history_status?.visibility = View.GONE + } + } + }) + } + danar_history_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { + val selected = danar_history_spinner?.selectedItem as TypeList? ?: return + loadDataFromDB(selected.type) + showingType = selected.type + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + clearCardView() + } + } + } + + inner class RecyclerViewAdapter internal constructor(private var historyList: List) : RecyclerView.Adapter() { + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): HistoryViewHolder = + HistoryViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.danar_history_item, viewGroup, false)) + + override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) { + val record = historyList[position] + holder.time.text = DateUtil.dateAndTimeString(record.recordDate) + holder.value.text = DecimalFormatter.to2Decimal(record.recordValue) + holder.stringValue.text = record.stringRecordValue + holder.bolusType.text = record.bolusType + holder.duration.text = DecimalFormatter.to0Decimal(record.recordDuration.toDouble()) + holder.alarm.text = record.recordAlarm + when (showingType) { + RecordTypes.RECORD_TYPE_ALARM -> { + holder.time.visibility = View.VISIBLE + holder.value.visibility = View.VISIBLE + holder.stringValue.visibility = View.GONE + holder.bolusType.visibility = View.GONE + holder.duration.visibility = View.GONE + holder.dailyBasal.visibility = View.GONE + holder.dailyBolus.visibility = View.GONE + holder.dailyTotal.visibility = View.GONE + holder.alarm.visibility = View.VISIBLE + } + + RecordTypes.RECORD_TYPE_BOLUS -> { + holder.time.visibility = View.VISIBLE + holder.value.visibility = View.VISIBLE + holder.stringValue.visibility = View.GONE + holder.bolusType.visibility = View.VISIBLE + holder.duration.visibility = View.VISIBLE + holder.dailyBasal.visibility = View.GONE + holder.dailyBolus.visibility = View.GONE + holder.dailyTotal.visibility = View.GONE + holder.alarm.visibility = View.GONE + } + + RecordTypes.RECORD_TYPE_DAILY -> { + holder.dailyBasal.text = MainApp.gs(R.string.formatinsulinunits, record.recordDailyBasal) + holder.dailyBolus.text = MainApp.gs(R.string.formatinsulinunits, record.recordDailyBolus) + holder.dailyTotal.text = MainApp.gs(R.string.formatinsulinunits, record.recordDailyBolus + record.recordDailyBasal) + holder.time.text = DateUtil.dateString(record.recordDate) + holder.time.visibility = View.VISIBLE + holder.value.visibility = View.GONE + holder.stringValue.visibility = View.GONE + holder.bolusType.visibility = View.GONE + holder.duration.visibility = View.GONE + holder.dailyBasal.visibility = View.VISIBLE + holder.dailyBolus.visibility = View.VISIBLE + holder.dailyTotal.visibility = View.VISIBLE + holder.alarm.visibility = View.GONE + } + + RecordTypes.RECORD_TYPE_GLUCOSE -> { + holder.value.text = Profile.toUnitsString(record.recordValue, record.recordValue * Constants.MGDL_TO_MMOLL, ProfileFunctions.getSystemUnits()) + holder.time.visibility = View.VISIBLE + holder.value.visibility = View.VISIBLE + holder.stringValue.visibility = View.GONE + holder.bolusType.visibility = View.GONE + holder.duration.visibility = View.GONE + holder.dailyBasal.visibility = View.GONE + holder.dailyBolus.visibility = View.GONE + holder.dailyTotal.visibility = View.GONE + holder.alarm.visibility = View.GONE + } + + RecordTypes.RECORD_TYPE_CARBO, RecordTypes.RECORD_TYPE_BASALHOUR, RecordTypes.RECORD_TYPE_ERROR, RecordTypes.RECORD_TYPE_PRIME, RecordTypes.RECORD_TYPE_REFILL, RecordTypes.RECORD_TYPE_TB -> { + holder.time.visibility = View.VISIBLE + holder.value.visibility = View.VISIBLE + holder.stringValue.visibility = View.GONE + holder.bolusType.visibility = View.GONE + holder.duration.visibility = View.GONE + holder.dailyBasal.visibility = View.GONE + holder.dailyBolus.visibility = View.GONE + holder.dailyTotal.visibility = View.GONE + holder.alarm.visibility = View.GONE + } + + RecordTypes.RECORD_TYPE_SUSPEND -> { + holder.time.visibility = View.VISIBLE + holder.value.visibility = View.GONE + holder.stringValue.visibility = View.VISIBLE + holder.bolusType.visibility = View.GONE + holder.duration.visibility = View.GONE + holder.dailyBasal.visibility = View.GONE + holder.dailyBolus.visibility = View.GONE + holder.dailyTotal.visibility = View.GONE + holder.alarm.visibility = View.GONE + } + } + } + + override fun getItemCount(): Int { + return historyList.size + } + + inner class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + var time: TextView = itemView.findViewById(R.id.danar_history_time) + var value: TextView = itemView.findViewById(R.id.danar_history_value) + var bolusType: TextView = itemView.findViewById(R.id.danar_history_bolustype) + var stringValue: TextView = itemView.findViewById(R.id.danar_history_stringvalue) + var duration: TextView = itemView.findViewById(R.id.danar_history_duration) + var dailyBasal: TextView = itemView.findViewById(R.id.danar_history_dailybasal) + var dailyBolus: TextView = itemView.findViewById(R.id.danar_history_dailybolus) + var dailyTotal: TextView = itemView.findViewById(R.id.danar_history_dailytotal) + var alarm: TextView = itemView.findViewById(R.id.danar_history_alarm) + } + } + + private fun loadDataFromDB(type: Byte) { + historyList = MainApp.getDbHelper().getDanaRHistoryRecordsByType(type) + runOnUiThread { danar_history_recyclerview?.swapAdapter(RecyclerViewAdapter(historyList), false) } + } + + private fun clearCardView() { + historyList = ArrayList() + runOnUiThread { danar_history_recyclerview?.swapAdapter(RecyclerViewAdapter(historyList), false) } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRNSHistorySync.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRNSHistorySync.java index dcb182a0d6..bf6f80af0b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRNSHistorySync.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRNSHistorySync.java @@ -184,7 +184,7 @@ public class DanaRNSHistorySync { log.debug("Syncing glucose record " + record.recordValue + " " + DateUtil.toISOString(record.recordDate)); nsrec.put(DANARSIGNATURE, record.bytes); nsrec.put("eventType", "BG Check"); - nsrec.put("glucose", Profile.fromMgdlToUnits(record.recordValue, ProfileFunctions.getInstance().getProfileUnits())); + nsrec.put("glucose", Profile.fromMgdlToUnits(record.recordValue, ProfileFunctions.getSystemUnits())); nsrec.put("glucoseType", "Finger"); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.java deleted file mode 100644 index f1181bb7cb..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.java +++ /dev/null @@ -1,214 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaR.activities; - -import android.os.Bundle; -import android.widget.Button; -import android.widget.RadioButton; -import android.widget.RadioGroup; -import android.widget.Switch; - -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.activities.NoSplashActivity; -import info.nightscout.androidaps.events.EventInitializationChanged; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin; -import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; -import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin; -import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.NumberPicker; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -/** - * Created by Rumen Georgiev on 5/31/2018. - */ - -public class DanaRUserOptionsActivity extends NoSplashActivity { - private static Logger log = LoggerFactory.getLogger(L.PUMP); - private CompositeDisposable disposable = new CompositeDisposable(); - - Switch timeFormat; - Switch buttonScroll; - Switch beep; - RadioGroup pumpAlarm; - RadioButton pumpAlarmSound; - RadioButton pumpAlarmVibrate; - RadioButton pumpAlarmBoth; - Switch pumpUnits; - NumberPicker screenTimeout; - NumberPicker backlightTimeout; - NumberPicker shutdown; - NumberPicker lowReservoir; - Button saveToPumpButton; - // This is for Dana pumps only - boolean isRS = DanaRSPlugin.getPlugin().isEnabled(PluginType.PUMP); - boolean isDanaR = DanaRPlugin.getPlugin().isEnabled(PluginType.PUMP); - boolean isDanaRv2 = DanaRv2Plugin.getPlugin().isEnabled(PluginType.PUMP); - - @Override - protected synchronized void onResume() { - super.onResume(); - disposable.add(RxBus.INSTANCE - .toObservable(EventInitializationChanged.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> setData(), FabricPrivacy::logException) - ); - } - - @Override - protected synchronized void onPause() { - disposable.clear(); - super.onPause(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.danar_user_options); - - timeFormat = (Switch) findViewById(R.id.danar_timeformat); - buttonScroll = (Switch) findViewById(R.id.danar_buttonscroll); - beep = (Switch) findViewById(R.id.danar_beep); - pumpAlarm = (RadioGroup) findViewById(R.id.danar_pumpalarm); - pumpAlarmSound = (RadioButton) findViewById(R.id.danar_pumpalarm_sound); - pumpAlarmVibrate = (RadioButton) findViewById(R.id.danar_pumpalarm_vibrate); - pumpAlarmBoth = (RadioButton) findViewById(R.id.danar_pumpalarm_both); - screenTimeout = (NumberPicker) findViewById(R.id.danar_screentimeout); - backlightTimeout = (NumberPicker) findViewById(R.id.danar_backlight); - pumpUnits = (Switch) findViewById(R.id.danar_units); - shutdown = (NumberPicker) findViewById(R.id.danar_shutdown); - lowReservoir = (NumberPicker) findViewById(R.id.danar_lowreservoir); - saveToPumpButton = (Button) findViewById(R.id.save_user_options); - - saveToPumpButton.setOnClickListener(v -> onSaveClick()); - - DanaRPump pump = DanaRPump.getInstance(); - //used for debugging - if (L.isEnabled(L.PUMP)) - log.debug("UserOptionsLoaded:" + (System.currentTimeMillis() - pump.lastConnection) / 1000 + " s ago" - + "\ntimeDisplayType:" + pump.timeDisplayType - + "\nbuttonScroll:" + pump.buttonScrollOnOff - + "\ntimeDisplayType:" + pump.timeDisplayType - + "\nlcdOnTimeSec:" + pump.lcdOnTimeSec - + "\nbacklight:" + pump.backlightOnTimeSec - + "\npumpUnits:" + pump.units - + "\nlowReservoir:" + pump.lowReservoirRate); - - screenTimeout.setParams((double) pump.lcdOnTimeSec, 5d, 240d, 5d, new DecimalFormat("1"), false, findViewById(R.id.ok)); - backlightTimeout.setParams((double) pump.backlightOnTimeSec, 1d, 60d, 1d, new DecimalFormat("1"), false, findViewById(R.id.ok)); - shutdown.setParams((double) pump.shutdownHour, 0d, 24d, 1d, new DecimalFormat("1"), true, findViewById(R.id.ok)); - lowReservoir.setParams((double) pump.lowReservoirRate, 10d, 60d, 10d, new DecimalFormat("10"), false, findViewById(R.id.ok)); - switch (pump.beepAndAlarm) { - case 0x01: - pumpAlarmSound.setChecked(true); - break; - case 0x02: - pumpAlarmVibrate.setChecked(true); - break; - case 0x11: - pumpAlarmBoth.setChecked(true); - break; - case 0x101: - pumpAlarmSound.setChecked(true); - beep.setChecked(true); - break; - case 0x110: - pumpAlarmVibrate.setChecked(true); - beep.setChecked(true); - break; - case 0x111: - pumpAlarmBoth.setChecked(true); - beep.setChecked(true); - break; - } - if (pump.lastSettingsRead == 0) - log.error("No settings loaded from pump!"); - else - setData(); - } - - public void setData() { - DanaRPump pump = DanaRPump.getInstance(); - // in DanaRS timeDisplay values are reversed - timeFormat.setChecked((!isRS && pump.timeDisplayType != 0) || (isRS && pump.timeDisplayType == 0)); - buttonScroll.setChecked(pump.buttonScrollOnOff != 0); - beep.setChecked(pump.beepAndAlarm > 4); - screenTimeout.setValue((double) pump.lcdOnTimeSec); - backlightTimeout.setValue((double) pump.backlightOnTimeSec); - pumpUnits.setChecked(pump.getUnits() != null && pump.getUnits().equals(Constants.MMOL)); - shutdown.setValue((double) pump.shutdownHour); - lowReservoir.setValue((double) pump.lowReservoirRate); - } - - public void onSaveClick() { - if (!isRS && !isDanaR && !isDanaRv2) { - //exit if pump is not DanaRS, Dana!, or DanaR with upgraded firmware - return; - } - DanaRPump pump = DanaRPump.getInstance(); - if (timeFormat.isChecked()) - pump.timeDisplayType = 1; - else - pump.timeDisplayType = 0; - // displayTime on RS is reversed - if (isRS) { - if (timeFormat.isChecked()) - pump.timeDisplayType = 0; - else - pump.timeDisplayType = 1; - } - if (buttonScroll.isChecked()) - pump.buttonScrollOnOff = 1; - else - pump.buttonScrollOnOff = 0; - - pump.beepAndAlarm = 1; // default - if (pumpAlarmSound.isChecked()) pump.beepAndAlarm = 1; - else if (pumpAlarmVibrate.isChecked()) pump.beepAndAlarm = 2; - else if (pumpAlarmBoth.isChecked()) pump.beepAndAlarm = 3; - if (beep.isChecked()) pump.beepAndAlarm += 4; - - - // step is 5 seconds - int screenTimeoutValue = !screenTimeout.getText().isEmpty() ? (Integer.parseInt(screenTimeout.getText().toString()) / 5) * 5 : 5; - if (screenTimeoutValue > 4 && screenTimeoutValue < 241) { - pump.lcdOnTimeSec = screenTimeoutValue; - } else { - pump.lcdOnTimeSec = 5; - } - int backlightTimeoutValue = !backlightTimeout.getText().isEmpty() ? Integer.parseInt(backlightTimeout.getText().toString()) : 1; - if (backlightTimeoutValue > 0 && backlightTimeoutValue < 61) { - pump.backlightOnTimeSec = backlightTimeoutValue; - } - if (pumpUnits.isChecked()) { - pump.units = 1; - } else { - pump.units = 0; - } - int shutDownValue = !shutdown.getText().isEmpty() ? Integer.parseInt(shutdown.getText().toString()) : 0; - if (shutDownValue > -1 && shutDownValue < 25) { - pump.shutdownHour = shutDownValue; - } else { - pump.shutdownHour = 0; - } - int lowReservoirValue = !lowReservoir.getText().isEmpty() ? (Integer.parseInt(lowReservoir.getText().toString()) * 10) / 10 : 10; - if (lowReservoirValue > 9 && lowReservoirValue < 51) { - pump.lowReservoirRate = lowReservoirValue; - } else - pump.lowReservoirRate = 10; - - ConfigBuilderPlugin.getPlugin().getCommandQueue().setUserOptions(null); - finish(); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.kt new file mode 100644 index 0000000000..a3a6c3c083 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.kt @@ -0,0 +1,159 @@ +package info.nightscout.androidaps.plugins.pump.danaR.activities + +import android.content.Intent +import android.os.Bundle +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity +import info.nightscout.androidaps.events.EventInitializationChanged +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus.toObservable +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin +import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump +import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin +import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.FabricPrivacy +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.danar_user_options_activity.* +import org.slf4j.LoggerFactory +import java.text.DecimalFormat +import kotlin.math.max +import kotlin.math.min + +class DanaRUserOptionsActivity : NoSplashAppCompatActivity() { + + private val log = LoggerFactory.getLogger(L.PUMP) + + private val disposable = CompositeDisposable() + + // This is for Dana pumps only + private var isRS = DanaRSPlugin.getPlugin().isEnabled(PluginType.PUMP) + private var isDanaR = DanaRPlugin.getPlugin().isEnabled(PluginType.PUMP) + private var isDanaRv2 = DanaRv2Plugin.getPlugin().isEnabled(PluginType.PUMP) + + @Synchronized + override fun onResume() { + super.onResume() + disposable.add(toObservable(EventInitializationChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ setData() }) { FabricPrivacy.logException(it) } + ) + } + + @Synchronized + override fun onPause() { + disposable.clear() + super.onPause() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.danar_user_options_activity) + + save_user_options.setOnClickListener { onSaveClick() } + val pump = DanaRPump.getInstance() + + if (L.isEnabled(L.PUMP)) + log.debug("UserOptionsLoaded:" + (System.currentTimeMillis() - pump.lastConnection) / 1000 + " s ago" + + "\ntimeDisplayType:" + pump.timeDisplayType + + "\nbuttonScroll:" + pump.buttonScrollOnOff + + "\ntimeDisplayType:" + pump.timeDisplayType + + "\nlcdOnTimeSec:" + pump.lcdOnTimeSec + + "\nbackLight:" + pump.backlightOnTimeSec + + "\npumpUnits:" + pump.units + + "\nlowReservoir:" + pump.lowReservoirRate) + + danar_screentimeout.setParams(pump.lcdOnTimeSec.toDouble(), 5.0, 240.0, 5.0, DecimalFormat("1"), false, save_user_options) + danar_backlight.setParams(pump.backlightOnTimeSec.toDouble(), 1.0, 60.0, 1.0, DecimalFormat("1"), false, save_user_options) + danar_shutdown.setParams(pump.shutdownHour.toDouble(), 0.0, 24.0, 1.0, DecimalFormat("1"), true, save_user_options) + danar_lowreservoir.setParams(pump.lowReservoirRate.toDouble(), 10.0, 60.0, 10.0, DecimalFormat("10"), false, save_user_options) + when (pump.beepAndAlarm) { + 0x01 -> danar_pumpalarm_sound.isChecked = true + 0x02 -> danar_pumpalarm_vibrate.isChecked = true + 0x11 -> danar_pumpalarm_both.isChecked = true + + 0x101 -> { + danar_pumpalarm_sound.isChecked = true + danar_beep.isChecked = true + } + + 0x110 -> { + danar_pumpalarm_vibrate.isChecked = true + danar_beep.isChecked = true + } + + 0x111 -> { + danar_pumpalarm_both.isChecked = true + danar_beep.isChecked = true + } + } + if (pump.lastSettingsRead == 0L) + log.error("No settings loaded from pump!") else setData() + } + + fun setData() { + val pump = DanaRPump.getInstance() + // in DanaRS timeDisplay values are reversed + danar_timeformat.isChecked = !isRS && pump.timeDisplayType != 0 || isRS && pump.timeDisplayType == 0 + danar_buttonscroll.isChecked = pump.buttonScrollOnOff != 0 + danar_beep.isChecked = pump.beepAndAlarm > 4 + danar_screentimeout.value = pump.lcdOnTimeSec.toDouble() + danar_backlight.value = pump.backlightOnTimeSec.toDouble() + danar_units.isChecked = pump.getUnits() == Constants.MMOL + danar_shutdown.value = pump.shutdownHour.toDouble() + danar_lowreservoir.value = pump.lowReservoirRate.toDouble() + } + + private fun onSaveClick() { + //exit if pump is not DanaRS, DanaR, or DanaR with upgraded firmware + if (!isRS && !isDanaR && !isDanaRv2) return + + val pump = DanaRPump.getInstance() + + if (isRS) // displayTime on RS is reversed + pump.timeDisplayType = if (danar_timeformat.isChecked) 0 else 1 + else + pump.timeDisplayType = if (danar_timeformat.isChecked) 1 else 0 + + pump.buttonScrollOnOff = if (danar_buttonscroll.isChecked) 1 else 0 + pump.beepAndAlarm = when { + danar_pumpalarm_sound.isChecked -> 1 + danar_pumpalarm_vibrate.isChecked -> 2 + danar_pumpalarm_both.isChecked -> 3 + else -> 1 + } + if (danar_beep.isChecked) pump.beepAndAlarm += 4 + + // step is 5 seconds, 5 to 240 + pump.lcdOnTimeSec = min(max(danar_screentimeout.value.toInt() / 5 * 5, 5), 240) + // 1 to 60 + pump.backlightOnTimeSec = min(max(danar_backlight.value.toInt(), 1), 60) + + pump.units = if (danar_units.isChecked) 1 else 0 + + pump.shutdownHour = min(danar_shutdown.value.toInt(),24) + + // 10 to 50 + pump.lowReservoirRate = min(max(danar_lowreservoir.value.toInt() * 10 / 10, 10), 50) + + ConfigBuilderPlugin.getPlugin().commandQueue.setUserOptions(object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.pumperror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + finish() + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java index 0801d8a173..848abd9b52 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java @@ -24,7 +24,7 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; +import info.nightscout.androidaps.dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -383,7 +383,7 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { try { o.wait(); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } } } else { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java index bb1f226730..0032a7e5e0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java @@ -24,7 +24,7 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; +import info.nightscout.androidaps.dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java index 7b2668d45c..8c8780d021 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java @@ -5,10 +5,12 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.preference.Preference; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; +import org.jetbrains.annotations.NotNull; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -32,7 +34,6 @@ import info.nightscout.androidaps.interfaces.DanaRInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; @@ -68,7 +69,7 @@ import io.reactivex.schedulers.Schedulers; * Created by mike on 03.09.2017. */ -public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInterface, ConstraintsInterface, ProfileInterface { +public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInterface, ConstraintsInterface { private Logger log = LoggerFactory.getLogger(L.PUMP); private CompositeDisposable disposable = new CompositeDisposable(); @@ -110,6 +111,14 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte } } + @Override + public void updatePreferenceSummary(@NotNull Preference pref) { + super.updatePreferenceSummary(pref); + + if (pref.getKey().equals(MainApp.gs(R.string.key_danars_name))) + pref.setSummary(SP.getString(R.string.key_danars_name, "")); + } + @Override protected void onStart() { Context context = MainApp.instance().getApplicationContext(); @@ -265,26 +274,6 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte return applyBolusConstraints(insulin); } - // Profile interface - - @Nullable - @Override - public ProfileStore getProfile() { - if (DanaRPump.getInstance().lastSettingsRead == 0) - return null; // no info now - return DanaRPump.getInstance().createConvertedProfile(); - } - - @Override - public String getUnits() { - return DanaRPump.getInstance().getUnits(); - } - - @Override - public String getProfileName() { - return DanaRPump.getInstance().createConvertedProfileName(); - } - // Pump interface @Override @@ -349,8 +338,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte int basalIncrement = pump.basal48Enable ? 30 * 60 : 60 * 60; for (int h = 0; h < basalValues; h++) { Double pumpValue = pump.pumpProfiles[pump.activeProfile][h]; - Double profileValue = profile.getBasalTimeFromMidnight((Integer) (h * basalIncrement)); - if (profileValue == null) return true; + Double profileValue = profile.getBasalTimeFromMidnight(h * basalIncrement); if (Math.abs(pumpValue - profileValue) > getPumpDescription().basalStep) { if (L.isEnabled(L.PUMP)) log.debug("Diff found. Hour: " + h + " Pump: " + pumpValue + " Profile: " + profileValue); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java index 42e910f291..87c904212c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java @@ -5,6 +5,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; +import android.content.pm.ActivityInfo; import android.os.Bundle; import android.os.Handler; import android.view.View; @@ -24,22 +25,20 @@ import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSDeviceCh import info.nightscout.androidaps.utils.SP; public class BLEScanActivity extends NoSplashAppCompatActivity { - private ListView listView = null; private ListAdapter mListAdapter = null; private ArrayList mDevices = new ArrayList<>(); - private BluetoothAdapter mBluetoothAdapter = null; private BluetoothLeScanner mBluetoothLeScanner = null; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.danars_blescanner_activity); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); mListAdapter = new ListAdapter(); - listView = (ListView) findViewById(R.id.danars_blescanner_listview); + ListView listView = findViewById(R.id.danars_blescanner_listview); listView.setEmptyView(findViewById(R.id.danars_blescanner_nodevice)); listView.setAdapter(mListAdapter); @@ -50,8 +49,9 @@ public class BLEScanActivity extends NoSplashAppCompatActivity { protected void onResume() { super.onResume(); - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter != null) { + if (!mBluetoothAdapter.isEnabled()) mBluetoothAdapter.enable(); mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); if (mBluetoothLeScanner == null) { @@ -89,11 +89,7 @@ public class BLEScanActivity extends NoSplashAppCompatActivity { } mDevices.add(item); - new Handler().post(new Runnable() { - public void run() { - mListAdapter.notifyDataSetChanged(); - } - }); + new Handler().post(() -> mListAdapter.notifyDataSetChanged()); } private ScanCallback mBleScanCallback = new ScanCallback() { @@ -134,19 +130,19 @@ public class BLEScanActivity extends NoSplashAppCompatActivity { } BluetoothDeviceItem item = getItem(i); - holder.setData(i, item); + holder.setData(item); return v; } private class ViewHolder implements View.OnClickListener { private BluetoothDeviceItem item = null; - private TextView mName = null; - private TextView mAddress = null; + private TextView mName; + private TextView mAddress; - public ViewHolder(View v) { - mName = (TextView) v.findViewById(R.id.ble_name); - mAddress = (TextView) v.findViewById(R.id.ble_address); + ViewHolder(View v) { + mName = v.findViewById(R.id.ble_name); + mAddress = v.findViewById(R.id.ble_address); v.setOnClickListener(ViewHolder.this); } @@ -160,7 +156,7 @@ public class BLEScanActivity extends NoSplashAppCompatActivity { finish(); } - public void setData(int pos, BluetoothDeviceItem data) { + public void setData(BluetoothDeviceItem data) { if (data != null) { try { String tTitle = data.device.getName(); @@ -174,7 +170,7 @@ public class BLEScanActivity extends NoSplashAppCompatActivity { mAddress.setText(data.device.getAddress()); item = data; - } catch (Exception e) { + } catch (Exception ignored) { } } } @@ -185,14 +181,14 @@ public class BLEScanActivity extends NoSplashAppCompatActivity { private class BluetoothDeviceItem { private BluetoothDevice device; - public BluetoothDeviceItem(BluetoothDevice device) { + BluetoothDeviceItem(BluetoothDevice device) { super(); this.device = device; } @Override public boolean equals(Object o) { - if (device == null || o == null || !(o instanceof BluetoothDeviceItem)) { + if (device == null || !(o instanceof BluetoothDeviceItem)) { return false; } BluetoothDeviceItem checkItem = (BluetoothDeviceItem) o; @@ -202,7 +198,7 @@ public class BLEScanActivity extends NoSplashAppCompatActivity { return stringEquals(device.getAddress(), checkItem.device.getAddress()); } - public boolean stringEquals(String arg1, String arg2) { + boolean stringEquals(String arg1, String arg2) { try { return arg1.equals(arg2); } catch (Exception e) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.java deleted file mode 100644 index 5be6d7fbd1..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.java +++ /dev/null @@ -1,16 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRS.activities; - -import android.os.Bundle; - -import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; - -public class PairingHelperActivity extends NoSplashAppCompatActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - PairingProgressDialog bolusProgressDialog = new PairingProgressDialog(); - bolusProgressDialog.setHelperActivity(this); - bolusProgressDialog.show(this.getSupportFragmentManager(), "PairingProgress"); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.kt new file mode 100644 index 0000000000..ba73e1b368 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.kt @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.pump.danaRS.activities + +import android.content.pm.ActivityInfo +import android.os.Bundle +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity +import info.nightscout.androidaps.plugins.pump.danaRS.dialogs.PairingProgressDialog + +class PairingHelperActivity : NoSplashAppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + PairingProgressDialog() + .setHelperActivity(this) + .show(supportFragmentManager, "PairingProgress") + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java index 3d59b34e16..55a2e0c51e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java @@ -4,11 +4,15 @@ import android.annotation.TargetApi; import android.os.Build; import com.cozmo.danar.util.BleCommandUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; import java.util.Date; public class DanaRS_Packet { + private static final Logger log = LoggerFactory.getLogger(DanaRS_Packet.class); + protected static final int TYPE_START = 0; protected static final int OPCODE_START = 1; protected static final int DATA_START = 2; @@ -73,7 +77,7 @@ public class DanaRS_Packet { return ret; } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } return null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingProgressDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/dialogs/PairingProgressDialog.java similarity index 75% rename from app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingProgressDialog.java rename to app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/dialogs/PairingProgressDialog.java index afb5e86387..bad9e98112 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingProgressDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/dialogs/PairingProgressDialog.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.pump.danaRS.activities; +package info.nightscout.androidaps.plugins.pump.danaRS.dialogs; import android.app.Activity; @@ -8,6 +8,8 @@ import android.os.HandlerThread; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; @@ -17,23 +19,22 @@ import androidx.fragment.app.DialogFragment; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.danaRS.activities.PairingHelperActivity; import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSPairingSuccess; import info.nightscout.androidaps.utils.FabricPrivacy; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; -public class PairingProgressDialog extends DialogFragment implements View.OnClickListener { +public class PairingProgressDialog extends DialogFragment { private CompositeDisposable disposable = new CompositeDisposable(); - TextView statusView; - ProgressBar progressBar; - Button button; - PairingHelperActivity helperActivity; + private TextView statusView; + private ProgressBar progressBar; + private Button button; + private PairingHelperActivity helperActivity; - static int secondsPassed = 0; - public static boolean pairingEnded = false; - public static boolean running = true; + private static boolean pairingEnded = false; private static Handler sHandler; private static HandlerThread sHandlerThread; @@ -46,7 +47,6 @@ public class PairingProgressDialog extends DialogFragment implements View.OnClic sHandlerThread.start(); sHandler = new Handler(sHandlerThread.getLooper()); } - secondsPassed = 0; } @@ -54,17 +54,21 @@ public class PairingProgressDialog extends DialogFragment implements View.OnClic public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.danars_pairingprogressdialog, container, false); - getDialog().setTitle(MainApp.gs(R.string.pairing)); - statusView = (TextView) view.findViewById(R.id.danars_pairingprogress_status); - progressBar = (ProgressBar) view.findViewById(R.id.danars_pairingprogress_progressbar); - button = (Button) view.findViewById(R.id.ok); + + getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); + getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + setCancelable(false); + getDialog().setCanceledOnTouchOutside(false); + + statusView = view.findViewById(R.id.danars_pairingprogress_status); + progressBar = view.findViewById(R.id.danars_pairingprogress_progressbar); + button = view.findViewById(R.id.ok); progressBar.setMax(100); progressBar.setProgress(0); statusView.setText(MainApp.gs(R.string.waitingforpairing)); button.setVisibility(View.GONE); - button.setOnClickListener(this); - setCancelable(false); + button.setOnClickListener(v -> dismiss()); sHandler.post(() -> { for (int i = 0; i < 20; i++) { @@ -110,8 +114,8 @@ public class PairingProgressDialog extends DialogFragment implements View.OnClic .observeOn(Schedulers.io()) .subscribe(event -> pairingEnded = true, FabricPrivacy::logException) ); - running = true; if (pairingEnded) dismiss(); + getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } @Override @@ -126,16 +130,10 @@ public class PairingProgressDialog extends DialogFragment implements View.OnClic public void onPause() { super.onPause(); disposable.clear(); - running = false; } - public void setHelperActivity(PairingHelperActivity activity) { + public PairingProgressDialog setHelperActivity(PairingHelperActivity activity) { this.helperActivity = activity; - } - - @Override - public void onClick(View v) { - running = false; - dismiss(); + return this; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java index 8a43d6e35a..253b861811 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java @@ -551,7 +551,7 @@ public class BLEComm { break; } } catch (Exception e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } startSignatureFound = false; packetIsValid = false; @@ -635,7 +635,7 @@ public class BLEComm { message.wait(5000); } catch (InterruptedException e) { log.error("sendMessage InterruptedException", e); - e.printStackTrace(); + log.error("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/DanaRSService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/DanaRSService.java index 2850f08c8d..4e56133222 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/DanaRSService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/DanaRSService.java @@ -26,8 +26,8 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.dialogs.BolusProgressDialog; +import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -181,6 +181,20 @@ public class DanaRSService extends Service { RxBus.INSTANCE.send(new EventInitializationChanged()); return; } + long now = System.currentTimeMillis(); + if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + bleComm.sendMessage(new DanaRS_Packet_General_Get_Shipping_Information()); // serial no + bleComm.sendMessage(new DanaRS_Packet_General_Get_Pump_Check()); // firmware + bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Profile_Number()); + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Bolus_Option()); // isExtendedEnabled + bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Calculation_Information()); // target + bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_CIR_CF_Array()); + bleComm.sendMessage(new DanaRS_Packet_Option_Get_User_Option()); // Getting user options + danaRPump.lastSettingsRead = now; + } + if (L.isEnabled(L.PUMPCOMM)) log.debug("Pump time difference: " + timeDiff + " seconds"); if (Math.abs(timeDiff) > 3) { @@ -201,9 +215,13 @@ public class DanaRSService extends Service { RxBus.INSTANCE.send(new EventInitializationChanged()); return; } else { - waitForWholeMinute(); // Dana can set only whole minute - // add 10sec to be sure we are over minute (will be cutted off anyway) - bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date(DateUtil.now() + T.secs(10).msecs()))); + if (danaRPump.protocol >= 6) { + bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date())); + } else { + waitForWholeMinute(); // Dana can set only whole minute + // add 10sec to be sure we are over minute (will be cutted off anyway) + bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date(DateUtil.now() + T.secs(10).msecs()))); + } bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); timeDiff = (danaRPump.pumpTime - System.currentTimeMillis()) / 1000L; if (L.isEnabled(L.PUMPCOMM)) @@ -211,20 +229,6 @@ public class DanaRSService extends Service { } } - long now = System.currentTimeMillis(); - if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { - RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); - bleComm.sendMessage(new DanaRS_Packet_General_Get_Shipping_Information()); // serial no - bleComm.sendMessage(new DanaRS_Packet_General_Get_Pump_Check()); // firmware - bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Profile_Number()); - bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Bolus_Option()); // isExtendedEnabled - bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal - bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Calculation_Information()); // target - bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_CIR_CF_Array()); - bleComm.sendMessage(new DanaRS_Packet_Option_Get_User_Option()); // Getting user options - danaRPump.lastSettingsRead = now; - } - loadEvents(); RxBus.INSTANCE.send(new EventDanaRNewStatus()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java index e528a67fae..ef254580f9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java @@ -25,8 +25,8 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.dialogs.BolusProgressDialog; +import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java index 7baf9458c9..8dd425b12c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java @@ -507,7 +507,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con nextValue = profile.getBasalValues()[i + 1]; if (profileBlock.getDuration() * 60 != (nextValue != null ? nextValue.timeAsSeconds : 24 * 60 * 60) - basalValue.timeAsSeconds) return false; - if (Math.abs(profileBlock.getBasalAmount() - basalValue.value) > (basalValue.value > 5 ? 0.05 : 0.005)) + if (Math.abs(profileBlock.getBasalAmount() - basalValue.value) > (basalValue.value > 5 ? 0.051 : 0.0051)) return false; } return true; @@ -571,6 +571,15 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con detailedBolusInfo.date = insightBolusID.timestamp; detailedBolusInfo.source = Source.PUMP; detailedBolusInfo.pumpId = insightBolusID.id; + if (detailedBolusInfo.carbs > 0 && detailedBolusInfo.carbTime != 0) { + DetailedBolusInfo carbInfo = new DetailedBolusInfo(); + carbInfo.carbs = detailedBolusInfo.carbs; + carbInfo.date = detailedBolusInfo.date + detailedBolusInfo.carbTime * 60L * 1000L; + carbInfo.source = Source.USER; + TreatmentsPlugin.getPlugin().addToHistoryTreatment(carbInfo, false); + detailedBolusInfo.carbTime = 0; + detailedBolusInfo.carbs = 0; + } TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true); while (true) { synchronized ($bolusLock) { @@ -1524,9 +1533,15 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con data.put("created_at", DateUtil.toISOString(date)); data.put("eventType", CareportalEvent.NOTE); data.put("notes", note); + CareportalEvent careportalEvent = new CareportalEvent(); + careportalEvent.date = date; + careportalEvent.source = Source.USER; + careportalEvent.eventType = CareportalEvent.NOTE; + careportalEvent.json = data.toString(); + MainApp.getDbHelper().createOrUpdate(careportalEvent); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } } @@ -1552,9 +1567,15 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con if (!enteredBy.equals("")) data.put("enteredBy", enteredBy); data.put("created_at", DateUtil.toISOString(date)); data.put("eventType", event); + CareportalEvent careportalEvent = new CareportalEvent(); + careportalEvent.date = date; + careportalEvent.source = Source.USER; + careportalEvent.eventType = event; + careportalEvent.json = data.toString(); + MainApp.getDbHelper().createOrUpdate(careportalEvent); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java index 874c908ca5..f6b3dc588c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/history_events/HistoryEvent.java @@ -3,8 +3,11 @@ package info.nightscout.androidaps.plugins.pump.insight.app_layer.history.histor import info.nightscout.androidaps.plugins.pump.insight.ids.HistoryEventIDs; import info.nightscout.androidaps.plugins.pump.insight.utils.BOCUtil; import info.nightscout.androidaps.plugins.pump.insight.utils.ByteBuf; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class HistoryEvent implements Comparable { + private static final Logger log = LoggerFactory.getLogger(HistoryEvent.class); private int eventYear; private int eventMonth; @@ -22,10 +25,8 @@ public class HistoryEvent implements Comparable { else { try { event = eventClass.newInstance(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InstantiationException e) { - e.printStackTrace(); + } catch (IllegalAccessException | InstantiationException e) { + log.error("Unhandled exception", e); } } event.parseHeader(byteBuf); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java index ae6d754493..48780e2343 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -44,7 +44,7 @@ import info.nightscout.androidaps.plugins.common.ManufacturerType; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract; @@ -372,7 +372,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter refreshAnyStatusThatNeedsToBeRefreshed(); } - RxBus.INSTANCE.send(new EventMedtronicPumpValuesChanged()); + RxBus.INSTANCE.send(new EventMedtronicPumpValuesChanged()); } @@ -386,7 +386,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter RileyLinkServiceState rileyLinkServiceState = MedtronicUtil.getServiceState(); - if (rileyLinkServiceState==null) { + if (rileyLinkServiceState == null) { LOG.error("RileyLink unreachable. RileyLinkServiceState is null."); return false; } @@ -744,13 +744,13 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter ClockDTO clock = MedtronicUtil.getPumpTime(); - if (clock==null) { // retry + if (clock == null) { // retry medtronicUIComm.executeCommand(MedtronicCommandType.GetRealTimeClock); clock = MedtronicUtil.getPumpTime(); } - if (clock==null) + if (clock == null) return; int timeDiff = Math.abs(clock.timeDifference); @@ -866,6 +866,11 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter }).start(); } + long now = System.currentTimeMillis(); + + detailedBolusInfo.date = now; + detailedBolusInfo.deliverAt = now; // not sure about that one + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true); // we subtract insulin, exact amount will be visible with next remainingInsulin update. @@ -877,7 +882,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter // calculate time for bolus and set driver to busy for that time int bolusTime = (int) (detailedBolusInfo.insulin * 42.0d); - long time = System.currentTimeMillis() + (bolusTime * 1000); + long time = now + (bolusTime * 1000); this.busyTimestamps.add(time); setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, true); @@ -1065,10 +1070,10 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter @Override public PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes, Profile profile, boolean enforceNew) { - if (percent==0) { + if (percent == 0) { return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew); } else { - double absoluteValue = profile.getBasal() * (percent /100.0d); + double absoluteValue = profile.getBasal() * (percent / 100.0d); getMDTPumpStatus(); absoluteValue = pumpStatusLocal.pumpType.determineCorrectBasalSize(absoluteValue); LOG.warn("setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (%d). This will start setTempBasalAbsolute, with calculated value (%.3f). Result might not be 100% correct.", percent, absoluteValue); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java index 32ca320de2..71f387d176 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java @@ -412,25 +412,18 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder validEntries; - // private Object validValues; - public PumpHistoryResult(PumpHistoryEntry searchEntry, Long targetDate) { if (searchEntry != null) { /* @@ -109,9 +105,8 @@ public class PumpHistoryResult { if (unprocessedEntry.isAfter(this.searchDate)) { this.validEntries.add(unprocessedEntry); } else { - LOG.debug("PE. PumpHistoryResult. Not after.. Unprocessed Entry [year={},entry={}]", - DateTimeUtil.getYear(unprocessedEntry.atechDateTime), unprocessedEntry); - +// LOG.debug("PE. PumpHistoryResult. Not after.. Unprocessed Entry [year={},entry={}]", +// DateTimeUtil.getYear(unprocessedEntry.atechDateTime), unprocessedEntry); if (DateTimeUtil.getYear(unprocessedEntry.atechDateTime) > 2015) olderEntries++; } @@ -131,14 +126,6 @@ public class PumpHistoryResult { } - private void clearOrPrepareList() { - if (this.validEntries == null) - this.validEntries = new ArrayList<>(); - else - this.validEntries.clear(); - } - - public String toString() { return "PumpHistoryResult [unprocessed=" + (unprocessedEntries != null ? "" + unprocessedEntries.size() : "0") + // ", valid=" + (validEntries != null ? "" + validEntries.size() : "0") + // diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java index 1eed440cdc..39c44167a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -32,6 +32,7 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.pump.common.bolusInfo.DetailedBolusInfoStorage; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder; @@ -49,6 +50,7 @@ import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpSta import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; import info.nightscout.androidaps.plugins.treatments.Treatment; +import info.nightscout.androidaps.plugins.treatments.TreatmentService; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.SP; @@ -67,7 +69,6 @@ import info.nightscout.androidaps.utils.SP; // All things marked with "TODO: Fix db code" needs to be updated in new 2.5 database code public class MedtronicHistoryData { - private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); private List allHistory = null; @@ -77,6 +78,7 @@ public class MedtronicHistoryData { private boolean isInit = false; private Gson gson; + private Gson gsonCore; private DatabaseHelper databaseHelper = MainApp.getDbHelper(); private ClockDTO pumpTime; @@ -94,10 +96,15 @@ public class MedtronicHistoryData { public MedtronicHistoryData() { this.allHistory = new ArrayList<>(); this.gson = MedtronicUtil.gsonInstance; + this.gsonCore = MedtronicUtil.getGsonInstanceCore(); if (this.gson == null) { this.gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); } + + if (this.gsonCore == null) { + this.gsonCore = new GsonBuilder().create(); + } } @@ -521,9 +528,15 @@ public class MedtronicHistoryData { if (!enteredBy.equals("")) data.put("enteredBy", enteredBy); data.put("created_at", DateUtil.toISOString(date)); data.put("eventType", event); + CareportalEvent careportalEvent = new CareportalEvent(); + careportalEvent.date = date; + careportalEvent.source = Source.USER; + careportalEvent.eventType = event; + careportalEvent.json = data.toString(); + MainApp.getDbHelper().createOrUpdate(careportalEvent); NSUpload.uploadCareportalEntryToNS(data); } catch (JSONException e) { - e.printStackTrace(); + LOG.error("Unhandled exception", e); } } @@ -597,7 +610,7 @@ public class MedtronicHistoryData { if (doubleBolusDebug) LOG.debug("DoubleBolusDebug: List (before filter): {}, FromDb={}", gson.toJson(entryList), - gson.toJson(entriesFromHistory)); + gsonCore.toJson(entriesFromHistory)); filterOutAlreadyAddedEntries(entryList, entriesFromHistory); @@ -607,9 +620,11 @@ public class MedtronicHistoryData { return; } + filterOutNonInsulinEntries(entriesFromHistory); + if (doubleBolusDebug) LOG.debug("DoubleBolusDebug: List (after filter): {}, FromDb={}", gson.toJson(entryList), - gson.toJson(entriesFromHistory)); + gsonCore.toJson(entriesFromHistory)); if (isCollectionEmpty(entriesFromHistory)) { for (PumpHistoryEntry treatment : entryList) { @@ -634,6 +649,23 @@ public class MedtronicHistoryData { } + private void filterOutNonInsulinEntries(List entriesFromHistory) { + // when we try to pair PumpHistory with AAPS treatments, we need to ignore all non-insulin entries + List removeList = new ArrayList<>(); + + for (DbObjectBase dbObjectBase : entriesFromHistory) { + + Treatment treatment = (Treatment)dbObjectBase; + + if (RileyLinkUtil.isSame(treatment.insulin, 0d)) { + removeList.add(dbObjectBase); + } + } + + entriesFromHistory.removeAll(removeList); + } + + private void processTBREntries(List entryList) { Collections.reverse(entryList); @@ -862,6 +894,7 @@ public class MedtronicHistoryData { return; List removeTreatmentsFromHistory = new ArrayList<>(); + List removeTreatmentsFromPH = new ArrayList<>(); for (DbObjectBase treatment : treatmentsFromHistory) { @@ -879,11 +912,17 @@ public class MedtronicHistoryData { if (selectedBolus != null) { entryList.remove(selectedBolus); + removeTreatmentsFromPH.add(selectedBolus); removeTreatmentsFromHistory.add(treatment); } } } + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: filterOutAlreadyAddedEntries: PumpHistory={}, Treatments={}", + gson.toJson(removeTreatmentsFromPH), + gsonCore.toJson(removeTreatmentsFromHistory)); + treatmentsFromHistory.removeAll(removeTreatmentsFromHistory); } @@ -947,36 +986,23 @@ public class MedtronicHistoryData { } else { - DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(treatment.date, bolusDTO.getDeliveredAmount()); + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: addBolus(OldTreatment={}): Bolus={}", treatment, bolusDTO); + + treatment.source = Source.PUMP; + treatment.pumpId = bolus.getPumpId(); + treatment.insulin = bolusDTO.getDeliveredAmount(); + + TreatmentService.UpdateReturn updateReturn = TreatmentsPlugin.getPlugin().getService().createOrUpdateMedtronic(treatment, false); if (doubleBolusDebug) - LOG.debug("DoubleBolusDebug: addBolus(tretament={}): Bolus={}, DetailedBolusInfo={}", treatment, bolusDTO, detailedBolusInfo); - - if (detailedBolusInfo == null) { - detailedBolusInfo = new DetailedBolusInfo(); - - if (doubleBolusDebug) - LOG.debug("DoubleBolusDebug: detailedBolusInfoCouldNotBeRetrived !"); - } - - detailedBolusInfo.date = treatment.date; - detailedBolusInfo.source = Source.PUMP; - detailedBolusInfo.pumpId = bolus.getPumpId(); - detailedBolusInfo.insulin = bolusDTO.getDeliveredAmount(); - detailedBolusInfo.carbs = treatment.carbs; - - addCarbsFromEstimate(detailedBolusInfo, bolus); - - if (doubleBolusDebug) - LOG.debug("DoubleBolusDebug: addBolus(tretament!=null): DetailedBolusInfo(New)={}", detailedBolusInfo); - - boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); - - bolus.setLinkedObject(detailedBolusInfo); + LOG.debug("DoubleBolusDebug: addBolus(tretament!=null): NewTreatment={}, UpdateReturn={}", treatment, updateReturn); if (isLogEnabled()) - LOG.debug("editBolus - [date={},pumpId={}, insulin={}, newRecord={}]", detailedBolusInfo.date, - detailedBolusInfo.pumpId, detailedBolusInfo.insulin, newRecord); + LOG.debug("editBolus - [date={},pumpId={}, insulin={}, newRecord={}]", treatment.date, + treatment.pumpId, treatment.insulin, updateReturn.toString()); + + bolus.setLinkedObject(treatment); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java index 2d1a3a871e..7641e5c4cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java @@ -20,13 +20,13 @@ import java.util.ArrayList; import java.util.List; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.activities.NoSplashActivity; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryGroup; -public class MedtronicHistoryActivity extends NoSplashActivity { +public class MedtronicHistoryActivity extends NoSplashAppCompatActivity { private static Logger LOG = LoggerFactory.getLogger(L.PUMP); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java index 96bf0a5d44..2c7ee69c2f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java @@ -61,8 +61,7 @@ public class MedtronicUtil extends RileyLinkUtil { private static int doneBit = 1 << 7; private static ClockDTO pumpTime; public static Gson gsonInstance = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); - public static Gson gsonInstancePretty = new GsonBuilder().excludeFieldsWithoutExposeAnnotation() - .setPrettyPrinting().create(); + public static Gson gsonInstanceCore = new GsonBuilder().create(); private static BatteryType batteryType = BatteryType.None; @@ -70,8 +69,9 @@ public class MedtronicUtil extends RileyLinkUtil { return gsonInstance; } - public static Gson getGsonInstancePretty() { - return gsonInstancePretty; + + public static Gson getGsonInstanceCore() { + return gsonInstanceCore; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.java index dfc4bed25d..8cfa903fb8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.java @@ -137,7 +137,7 @@ public class SensitivityAAPSPlugin extends AbstractSensitivityPlugin { Double[] deviations = new Double[deviationsArray.size()]; deviations = deviationsArray.toArray(deviations); - double sens = profile.getIsf(); + double sens = profile.getIsfMgdl(); String ratioLimit = ""; String sensResult = ""; @@ -148,7 +148,7 @@ public class SensitivityAAPSPlugin extends AbstractSensitivityPlugin { Arrays.sort(deviations); double percentile = IobCobCalculatorPlugin.percentile(deviations, 0.50); - double basalOff = percentile * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + double basalOff = percentile * (60 / 5) / sens; double ratio = 1 + (basalOff / profile.getMaxDailyBasal()); if (percentile < 0) { // sensitive diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref0Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref0Plugin.java index cfcaf0eb22..6298724514 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref0Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref0Plugin.java @@ -131,7 +131,7 @@ public class SensitivityOref0Plugin extends AbstractSensitivityPlugin { Double[] deviations = new Double[deviationsArray.size()]; deviations = deviationsArray.toArray(deviations); - double sens = profile.getIsf(); + double sens = profile.getIsfMgdl(); double ratio = 1; String ratioLimit = ""; @@ -154,10 +154,10 @@ public class SensitivityOref0Plugin extends AbstractSensitivityPlugin { double basalOff = 0; if (pSensitive < 0) { // sensitive - basalOff = pSensitive * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + basalOff = pSensitive * (60 / 5.0) / sens; sensResult = "Excess insulin sensitivity detected"; } else if (pResistant > 0) { // resistant - basalOff = pResistant * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + basalOff = pResistant * (60 / 5.0) / sens; sensResult = "Excess insulin resistance detected"; } else { sensResult = "Sensitivity normal"; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.java index 5e0f5d3d85..131cafe89c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.java @@ -146,7 +146,7 @@ public class SensitivityOref1Plugin extends AbstractSensitivityPlugin { Double[] deviations = new Double[deviationsArray.size()]; deviations = deviationsArray.toArray(deviations); - double sens = profile.getIsf(); + double sens = profile.getIsfMgdl(); double ratio = 1; String ratioLimit = ""; @@ -174,10 +174,10 @@ public class SensitivityOref1Plugin extends AbstractSensitivityPlugin { double basalOff = 0; if (pSensitive < 0) { // sensitive - basalOff = pSensitive * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + basalOff = pSensitive * (60 / 5.0) / sens; sensResult = "Excess insulin sensitivity detected"; } else if (pResistant > 0) { // resistant - basalOff = pResistant * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + basalOff = pResistant * (60 / 5.0) / sens; sensResult = "Excess insulin resistance detected"; } else { sensResult = "Sensitivity normal"; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.java index 91b69162bb..5d0aac4d4c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.java @@ -164,7 +164,7 @@ public class SensitivityWeightedAveragePlugin extends AbstractSensitivityPlugin return new AutosensResult(); } - double sens = profile.getIsf(); + double sens = profile.getIsfMgdl(); String ratioLimit = ""; String sensResult; @@ -173,7 +173,7 @@ public class SensitivityWeightedAveragePlugin extends AbstractSensitivityPlugin log.debug("Records: " + index + " " + pastSensitivity); double average = weightedsum / weights; - double basalOff = average * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + double basalOff = average * (60 / 5.0) / sens; double ratio = 1 + (basalOff / profile.getMaxDailyBasal()); if (average < 0) { // sensitive diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.java index 6023d70d12..b67cadfed4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.source; -import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; import android.view.LayoutInflater; @@ -9,23 +8,22 @@ import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import java.util.List; -import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.T; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -36,19 +34,17 @@ import io.reactivex.disposables.CompositeDisposable; public class BGSourceFragment extends Fragment { private CompositeDisposable disposable = new CompositeDisposable(); - RecyclerView recyclerView; + private RecyclerView recyclerView; - String units = Constants.MGDL; - - final long MILLS_TO_THE_PAST = T.hours(12).msecs(); + private final long MILLS_TO_THE_PAST = T.hours(12).msecs(); @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { try { View view = inflater.inflate(R.layout.bgsource_fragment, container, false); - recyclerView = (RecyclerView) view.findViewById(R.id.bgsource_recyclerview); + recyclerView = view.findViewById(R.id.bgsource_recyclerview); recyclerView.setHasFixedSize(true); LinearLayoutManager llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); @@ -57,9 +53,6 @@ public class BGSourceFragment extends Fragment { RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().getAllBgreadingsDataFromTime(now - MILLS_TO_THE_PAST, false)); recyclerView.setAdapter(adapter); - if (ConfigBuilderPlugin.getPlugin().getActiveProfileInterface() != null && ConfigBuilderPlugin.getPlugin().getActiveProfileInterface().getProfile() != null && ConfigBuilderPlugin.getPlugin().getActiveProfileInterface().getProfile().getDefaultProfile() != null) - units = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface().getProfile().getDefaultProfile().getUnits(); - return view; } catch (Exception e) { FabricPrivacy.logException(e); @@ -97,6 +90,7 @@ public class BGSourceFragment extends Fragment { this.bgReadings = bgReadings; } + @NonNull @Override public BgReadingsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.bgsource_item, viewGroup, false); @@ -109,7 +103,7 @@ public class BGSourceFragment extends Fragment { holder.ns.setVisibility(NSUpload.isIdValid(bgReading._id) ? View.VISIBLE : View.GONE); holder.invalid.setVisibility(!bgReading.isValid ? View.VISIBLE : View.GONE); holder.date.setText(DateUtil.dateAndTimeString(bgReading.date)); - holder.value.setText(bgReading.valueToUnitsToString(units)); + holder.value.setText(bgReading.valueToUnitsToString(ProfileFunctions.getSystemUnits())); holder.direction.setText(bgReading.directionToSymbol()); holder.remove.setTag(bgReading); } @@ -119,7 +113,7 @@ public class BGSourceFragment extends Fragment { return bgReadings.size(); } - class BgReadingsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + class BgReadingsViewHolder extends RecyclerView.ViewHolder { TextView date; TextView value; TextView direction; @@ -129,46 +123,22 @@ public class BGSourceFragment extends Fragment { BgReadingsViewHolder(View itemView) { super(itemView); - date = (TextView) itemView.findViewById(R.id.bgsource_date); - value = (TextView) itemView.findViewById(R.id.bgsource_value); - direction = (TextView) itemView.findViewById(R.id.bgsource_direction); - invalid = (TextView) itemView.findViewById(R.id.invalid_sign); - ns = (TextView) itemView.findViewById(R.id.ns_sign); - remove = (TextView) itemView.findViewById(R.id.bgsource_remove); - remove.setOnClickListener(this); + date = itemView.findViewById(R.id.bgsource_date); + value = itemView.findViewById(R.id.bgsource_value); + direction = itemView.findViewById(R.id.bgsource_direction); + invalid = itemView.findViewById(R.id.invalid_sign); + ns = itemView.findViewById(R.id.ns_sign); + remove = itemView.findViewById(R.id.bgsource_remove); + remove.setOnClickListener(v -> { + final BgReading bgReading = (BgReading) v.getTag(); + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(bgReading.date) + "\n" + bgReading.valueToUnitsToString(ProfileFunctions.getSystemUnits()), () -> { + bgReading.isValid = false; + MainApp.getDbHelper().update(bgReading); + updateGUI(); + }); + }); remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); } - - @Override - public void onClick(View v) { - final BgReading bgReading = (BgReading) v.getTag(); - switch (v.getId()) { - - case R.id.bgsource_remove: - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(bgReading.date) + "\n" + bgReading.valueToUnitsToString(units)); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { -/* final String _id = bgReading._id; - if (NSUpload.isIdValid(_id)) { - NSUpload.removeFoodFromNS(_id); - } else { - UploadQueue.removeID("dbAdd", _id); - } -*/ - bgReading.isValid = false; - MainApp.getDbHelper().update(bgReading); - updateGUI(); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - - } - } } } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt new file mode 100644 index 0000000000..1f79b82bc6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt @@ -0,0 +1,77 @@ +package info.nightscout.androidaps.plugins.source + +import android.content.Intent +import android.os.Handler +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.db.BgReading +import info.nightscout.androidaps.interfaces.BgSourceInterface +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.pump.virtual.VirtualPumpPlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.isRunningTest +import org.slf4j.LoggerFactory +import java.util.* +import kotlin.math.PI +import kotlin.math.sin + +object RandomBgPlugin : PluginBase(PluginDescription() + .mainType(PluginType.BGSOURCE) + .fragmentClass(BGSourceFragment::class.java.name) + .pluginName(R.string.randombg) + .shortName(R.string.randombg_short) + .description(R.string.description_source_randombg)), BgSourceInterface { + + private val log = LoggerFactory.getLogger(L.BGSOURCE) + + private val loopHandler = Handler() + private lateinit var refreshLoop: Runnable + + const val interval = 1L // minutes + + init { + refreshLoop = Runnable { + handleNewData(Intent()) + loopHandler.postDelayed(refreshLoop, T.mins(interval).msecs()) + } + } + + override fun advancedFilteringSupported(): Boolean { + return false + } + + override fun onStart() { + super.onStart() + loopHandler.postDelayed(refreshLoop, T.mins(interval).msecs()) + } + + override fun onStop() { + super.onStop() + loopHandler.removeCallbacks(refreshLoop) + } + + override fun specialEnableCondition(): Boolean { + return isRunningTest() || VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP) && MainApp.engineeringMode + } + + override fun handleNewData(intent: Intent) { + if (!isEnabled(PluginType.BGSOURCE)) return + val min = 70 + val max = 190 + + val cal = GregorianCalendar() + val currentMinute = cal.get(Calendar.MINUTE) + (cal.get(Calendar.HOUR_OF_DAY) % 2) * 60 + val bgMgdl = min + (max - min) + (max - min) * sin(currentMinute / 120.0 * 2 * PI) + + val bgReading = BgReading() + bgReading.value = bgMgdl + bgReading.date = DateUtil.now() + bgReading.raw = bgMgdl + MainApp.getDbHelper().createIfNotExists(bgReading, "RandomBG") + log.debug("Generated BG: $bgReading") + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt index dfbe2fff39..bf5661ad00 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt @@ -9,6 +9,8 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.RequestDexcomPermissionActivity import info.nightscout.androidaps.db.BgReading import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.dialogs.CareDialog import info.nightscout.androidaps.interfaces.BgSourceInterface import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription @@ -17,6 +19,7 @@ import info.nightscout.androidaps.logging.L import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.SP +import info.nightscout.androidaps.utils.T import org.json.JSONObject import org.slf4j.LoggerFactory @@ -51,7 +54,7 @@ object SourceDexcomPlugin : PluginBase(PluginDescription() } fun findDexcomPackageName(): String? { - val packageManager = MainApp.instance().packageManager; + val packageManager = MainApp.instance().packageManager for (packageInfo in packageManager.getInstalledPackages(0)) { if (PACKAGE_NAMES.contains(packageInfo.packageName)) return packageInfo.packageName } @@ -64,43 +67,66 @@ object SourceDexcomPlugin : PluginBase(PluginDescription() val sensorType = intent.getStringExtra("sensorType") ?: "" val glucoseValues = intent.getBundleExtra("glucoseValues") for (i in 0 until glucoseValues.size()) { - val glucoseValue = glucoseValues.getBundle(i.toString()) - val bgReading = BgReading() - bgReading.value = glucoseValue!!.getInt("glucoseValue").toDouble() - bgReading.direction = glucoseValue.getString("trendArrow") - bgReading.date = glucoseValue.getLong("timestamp") * 1000 - bgReading.raw = 0.0 - if (MainApp.getDbHelper().createIfNotExists(bgReading, "Dexcom$sensorType")) { - if (SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - NSUpload.uploadBg(bgReading, "AndroidAPS-Dexcom$sensorType") - } - if (SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { - NSUpload.sendToXdrip(bgReading) + glucoseValues.getBundle(i.toString())?.let { glucoseValue -> + val bgReading = BgReading() + bgReading.value = glucoseValue.getInt("glucoseValue").toDouble() + bgReading.direction = glucoseValue.getString("trendArrow") + bgReading.date = glucoseValue.getLong("timestamp") * 1000 + bgReading.raw = 0.0 + if (MainApp.getDbHelper().createIfNotExists(bgReading, "Dexcom$sensorType")) { + if (SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { + NSUpload.uploadBg(bgReading, "AndroidAPS-Dexcom$sensorType") + } + if (SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { + NSUpload.sendToXdrip(bgReading) + } } } } val meters = intent.getBundleExtra("meters") for (i in 0 until meters.size()) { val meter = meters.getBundle(i.toString()) - val timestamp = meter!!.getLong("timestamp") * 1000 - if (MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp) != null) continue - val jsonObject = JSONObject() - jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") - jsonObject.put("created_at", DateUtil.toISOString(timestamp)) - jsonObject.put("eventType", CareportalEvent.BGCHECK) - jsonObject.put("glucoseType", "Finger") - jsonObject.put("glucose", meter.getInt("meterValue")) - jsonObject.put("units", Constants.MGDL) - NSUpload.uploadCareportalEntryToNS(jsonObject) + meter?.let { + val timestamp = it.getLong("timestamp") * 1000 + val now = DateUtil.now() + if (timestamp > now - T.months(1).msecs() && timestamp < now) + if (MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp) == null) { + val jsonObject = JSONObject() + jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") + jsonObject.put("created_at", DateUtil.toISOString(timestamp)) + jsonObject.put("eventType", CareportalEvent.BGCHECK) + jsonObject.put("glucoseType", "Finger") + jsonObject.put("glucose", meter.getInt("meterValue")) + jsonObject.put("units", Constants.MGDL) + + val careportalEvent = CareportalEvent() + careportalEvent.date = timestamp + careportalEvent.source = Source.USER + careportalEvent.eventType = CareportalEvent.BGCHECK + careportalEvent.json = jsonObject.toString() + MainApp.getDbHelper().createOrUpdate(careportalEvent) + NSUpload.uploadCareportalEntryToNS(jsonObject) + } + } } if (SP.getBoolean(R.string.key_dexcom_lognssensorchange, false) && intent.hasExtra("sensorInsertionTime")) { - val sensorInsertionTime = intent.extras!!.getLong("sensorInsertionTime") * 1000 - if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) { - val jsonObject = JSONObject() - jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") - jsonObject.put("created_at", DateUtil.toISOString(sensorInsertionTime)) - jsonObject.put("eventType", CareportalEvent.SENSORCHANGE) - NSUpload.uploadCareportalEntryToNS(jsonObject) + intent.extras?.let { + val sensorInsertionTime = it.getLong("sensorInsertionTime") * 1000 + val now = DateUtil.now() + if (sensorInsertionTime > now - T.months(1).msecs() && sensorInsertionTime < now) + if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) { + val jsonObject = JSONObject() + jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") + jsonObject.put("created_at", DateUtil.toISOString(sensorInsertionTime)) + jsonObject.put("eventType", CareportalEvent.SENSORCHANGE) + val careportalEvent = CareportalEvent() + careportalEvent.date = sensorInsertionTime + careportalEvent.source = Source.USER + careportalEvent.eventType = CareportalEvent.SENSORCHANGE + careportalEvent.json = jsonObject.toString() + MainApp.getDbHelper().createOrUpdate(careportalEvent) + NSUpload.uploadCareportalEntryToNS(jsonObject) + } } } } catch (e: Exception) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceEversensePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceEversensePlugin.java index 6f39e3d762..6d0067414a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceEversensePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceEversensePlugin.java @@ -15,6 +15,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.interfaces.BgSourceInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; @@ -143,6 +144,12 @@ public class SourceEversensePlugin extends PluginBase implements BgSourceInterfa data.put("glucoseType", "Finger"); data.put("glucose", calibrationGlucoseLevels[i]); data.put("units", Constants.MGDL); + CareportalEvent careportalEvent = new CareportalEvent(); + careportalEvent.date = calibrationTimestamps[i]; + careportalEvent.source = Source.USER; + careportalEvent.eventType = CareportalEvent.BGCHECK; + careportalEvent.json = data.toString(); + MainApp.getDbHelper().createOrUpdate(careportalEvent); NSUpload.uploadCareportalEntryToNS(data); } } catch (JSONException e) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.java index 0e7215b368..f22523160d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.java @@ -9,7 +9,7 @@ import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.T; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java index 7123b6c96b..1331c8eedf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java @@ -133,7 +133,7 @@ public class TreatmentService extends OrmLiteBaseService { try { getDao().executeRaw("ALTER TABLE `" + Treatment.TABLE_TREATMENTS + "` ADD COLUMN boluscalc STRING;"); } catch (SQLException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } } else { if (L.isEnabled(L.DATATREATMENTS)) @@ -147,7 +147,7 @@ public class TreatmentService extends OrmLiteBaseService { try { getDao().executeRaw("ALTER TABLE `" + Treatment.TABLE_TREATMENTS + "` DROP COLUMN boluscalc STRING;"); } catch (SQLException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } } } @@ -736,6 +736,14 @@ public class TreatmentService extends OrmLiteBaseService { boolean newRecord; boolean success; + + @Override + public String toString() { + return "UpdateReturn [" + + "newRecord=" + newRecord + + ", success=" + success + + ']'; + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java index 28181da190..8ec0b48ffa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java @@ -45,7 +45,7 @@ import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; @@ -103,11 +103,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface @Override protected void onStart() { - initializeTempBasalData(); - initializeTreatmentData(); - initializeExtendedBolusData(); - initializeTempTargetData(); - initializeProfileSwitchData(); + initializeData(range()); super.onStart(); disposable.add(RxBus.INSTANCE .toObservable(EventReloadTreatmentData.class) @@ -115,8 +111,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface .subscribe(event -> { if (L.isEnabled(L.DATATREATMENTS)) log.debug("EventReloadTreatmentData"); - initializeTreatmentData(); - initializeExtendedBolusData(); + initializeTreatmentData(range()); + initializeExtendedBolusData(range()); updateTotalIOBTreatments(); RxBus.INSTANCE.send(event.getNext()); }, @@ -125,13 +121,13 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface disposable.add(RxBus.INSTANCE .toObservable(EventReloadProfileSwitchData.class) .observeOn(Schedulers.io()) - .subscribe(event -> initializeProfileSwitchData(), + .subscribe(event -> initializeProfileSwitchData(range()), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE .toObservable(EventTempTargetChange.class) .observeOn(Schedulers.io()) - .subscribe(event -> initializeTempTargetData(), + .subscribe(event -> initializeTempTargetData(range()), FabricPrivacy::logException )); disposable.add(RxBus.INSTANCE @@ -140,7 +136,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface .subscribe(event -> { if (L.isEnabled(L.DATATREATMENTS)) log.debug("EventReloadTempBasalData"); - initializeTempBasalData(); + initializeTempBasalData(range()); updateTotalIOBTempBasals(); }, FabricPrivacy::logException @@ -157,61 +153,61 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return this.service; } - private void initializeTreatmentData() { + private long range() { + double dia = Constants.defaultDIA; + if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) + dia = ProfileFunctions.getInstance().getProfile().getDia(); + return (long) (60 * 60 * 1000L * (24 + dia)); + } + + public void initializeData(long range) { + initializeTempBasalData(range); + initializeTreatmentData(range); + initializeExtendedBolusData(range); + initializeTempTargetData(range); + initializeProfileSwitchData(range); + } + + private void initializeTreatmentData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTreatmentData"); - double dia = Constants.defaultDIA; - if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) - dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); synchronized (treatments) { treatments.clear(); - treatments.addAll(getService().getTreatmentDataFromTime(fromMills, false)); + treatments.addAll(getService().getTreatmentDataFromTime(DateUtil.now() - range, false)); } } - private void initializeTempBasalData() { + private void initializeTempBasalData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTempBasalData"); - double dia = Constants.defaultDIA; - if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) - dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); - synchronized (tempBasals) { - tempBasals.reset().add(MainApp.getDbHelper().getTemporaryBasalsDataFromTime(fromMills, false)); + tempBasals.reset().add(MainApp.getDbHelper().getTemporaryBasalsDataFromTime(DateUtil.now() - range, false)); } } - private void initializeExtendedBolusData() { + private void initializeExtendedBolusData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeExtendedBolusData"); - double dia = Constants.defaultDIA; - if (ConfigBuilderPlugin.getPlugin() != null && ProfileFunctions.getInstance().getProfile() != null) - dia = ProfileFunctions.getInstance().getProfile().getDia(); - long fromMills = (long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)); - synchronized (extendedBoluses) { - extendedBoluses.reset().add(MainApp.getDbHelper().getExtendedBolusDataFromTime(fromMills, false)); + extendedBoluses.reset().add(MainApp.getDbHelper().getExtendedBolusDataFromTime(DateUtil.now() - range, false)); } } - private void initializeTempTargetData() { + private void initializeTempTargetData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeTempTargetData"); synchronized (tempTargets) { - long fromMills = System.currentTimeMillis() - 60 * 60 * 1000L * 24; - tempTargets.reset().add(MainApp.getDbHelper().getTemptargetsDataFromTime(fromMills, false)); + tempTargets.reset().add(MainApp.getDbHelper().getTemptargetsDataFromTime(DateUtil.now() - range, false)); } } - private void initializeProfileSwitchData() { + private void initializeProfileSwitchData(long range) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("initializeProfileSwitchData"); synchronized (profiles) { - profiles.reset().add(MainApp.getDbHelper().getProfileSwitchData(false)); + profiles.reset().add(MainApp.getDbHelper().getProfileSwitchData(DateUtil.now() - range, false)); } } @@ -261,7 +257,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface if (!pumpInterface.isFakingTempsByExtendedBoluses()) synchronized (extendedBoluses) { - for (Integer pos = 0; pos < extendedBoluses.size(); pos++) { + for (int pos = 0; pos < extendedBoluses.size(); pos++) { ExtendedBolus e = extendedBoluses.get(pos); if (e.date > time) continue; IobTotal calc = e.iobCalc(time); @@ -349,7 +345,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface long time = System.currentTimeMillis(); synchronized (treatments) { if (MedtronicHistoryData.doubleBolusDebug) - log.debug("DoubleBolusDebug: AllTreatmentsInDb: {}", MedtronicUtil.getGsonInstance().toJson(treatments)); + log.debug("DoubleBolusDebug: AllTreatmentsInDb: {}", MedtronicUtil.getGsonInstanceCore().toJson(treatments)); for (Treatment t : treatments) { if (t.date <= time && t.date >= fromTimestamp) @@ -357,7 +353,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } if (MedtronicHistoryData.doubleBolusDebug) - log.debug("DoubleBolusDebug: FilteredTreatments: AfterTime={}, Items={}", fromTimestamp, MedtronicUtil.getGsonInstance().toJson(in5minback)); + log.debug("DoubleBolusDebug: FilteredTreatments: AfterTime={}, Items={}", fromTimestamp, MedtronicUtil.getGsonInstanceCore().toJson(in5minback)); return in5minback; } @@ -365,11 +361,10 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface @Override - public List getTreatments5MinBackFromHistory(long time) { + public List getCarbTreatments5MinBackFromHistory(long time) { List in5minback = new ArrayList<>(); synchronized (treatments) { - for (Integer pos = 0; pos < treatments.size(); pos++) { - Treatment t = treatments.get(pos); + for (Treatment t : treatments) { if (!t.isValid) continue; if (t.date <= time && t.date > time - 5 * 60 * 1000 && t.carbs > 0) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.kt index c65c75c00e..63f3b57f65 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.kt @@ -6,9 +6,13 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import androidx.fragment.app.DialogFragment +import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.JsonHelper +import info.nightscout.androidaps.utils.StringUtils import kotlinx.android.synthetic.main.treatments_wizardinfo_dialog.* import org.json.JSONObject @@ -30,34 +34,36 @@ class WizardInfoDialog : DialogFragment() { super.onViewCreated(view, savedInstanceState) close.setOnClickListener { dismiss() } - + val units = ProfileFunctions.getSystemUnits() + val bg_string: String + if (units.equals(Constants.MGDL)) { bg_string = DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "bg"))} else { bg_string = DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg"))} // BG - treatments_wizard_bg.text = DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg")) + " ISF: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "isf")) - treatments_wizard_bginsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulinbg")) + "U" + treatments_wizard_bg.text = MainApp.gs(R.string.format_bg_isf, bg_string , JsonHelper.safeGetDouble(json, "isf")) + treatments_wizard_bginsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "insulinbg")) treatments_wizard_bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused") treatments_wizard_ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused") // Trend treatments_wizard_bgtrend.text = JsonHelper.safeGetString(json, "trend") - treatments_wizard_bgtrendinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulintrend")) + "U" + treatments_wizard_bgtrendinsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "insulintrend")) treatments_wizard_bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused") // COB - treatments_wizard_cob.text = DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "cob")) + "g IC: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "ic")) - treatments_wizard_cobinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulincob")) + "U" + treatments_wizard_cob.text = MainApp.gs(R.string.format_cob_ic, JsonHelper.safeGetDouble(json, "cob"), JsonHelper.safeGetDouble(json, "ic")) + treatments_wizard_cobinsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "insulincob")) treatments_wizard_cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused") // Bolus IOB - treatments_wizard_bolusiobinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "bolusiob")) + "U" + treatments_wizard_bolusiobinsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "bolusiob")) treatments_wizard_bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused") // Basal IOB - treatments_wizard_basaliobinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "basaliob")) + "U" + treatments_wizard_basaliobinsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "basaliob")) treatments_wizard_basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused") // Superbolus - treatments_wizard_sbinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulinsuperbolus")) + "U" + treatments_wizard_sbinsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "insulinsuperbolus")) treatments_wizard_sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused") // Carbs - treatments_wizard_carbs.text = DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "carbs")) + "g IC: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "ic")) - treatments_wizard_carbsinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulincarbs")) + "U" + treatments_wizard_carbs.text = MainApp.gs(R.string.format_carbs_ic, JsonHelper.safeGetDouble(json, "carbs"), JsonHelper.safeGetDouble(json, "ic")) + treatments_wizard_carbsinsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "insulincarbs")) // Correction - treatments_wizard_correctioninsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "othercorrection")) + "U" + treatments_wizard_correctioninsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "othercorrection")) // Profile treatments_wizard_profile.text = JsonHelper.safeGetString(json, "profile") // Notes @@ -65,6 +71,6 @@ class WizardInfoDialog : DialogFragment() { // Percentage treatments_wizard_percent_used.text = DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "percentageCorrection", 100.0)) + "%" // Total - treatments_wizard_totalinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulin")) + "U" + treatments_wizard_totalinsulin.text = StringUtils.formatInsulin(JsonHelper.safeGetDouble(json, "insulin")) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.java index ee78c6748b..3c1d56afb7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.java @@ -1,7 +1,5 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.content.Context; -import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,7 +8,7 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; +import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -38,24 +36,21 @@ import info.nightscout.androidaps.plugins.treatments.dialogs.WizardInfoDialog; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import static info.nightscout.androidaps.utils.DateUtil.now; -public class TreatmentsBolusFragment extends Fragment implements View.OnClickListener { +public class TreatmentsBolusFragment extends Fragment { private CompositeDisposable disposable = new CompositeDisposable(); - RecyclerView recyclerView; - LinearLayoutManager llm; + private RecyclerView recyclerView; - TextView iobTotal; - TextView activityTotal; - Button refreshFromNS; - Button deleteFutureTreatments; - - Context context; + private TextView iobTotal; + private TextView activityTotal; + private Button deleteFutureTreatments; public class RecyclerViewAdapter extends RecyclerView.Adapter { @@ -65,6 +60,7 @@ public class TreatmentsBolusFragment extends Fragment implements View.OnClickLis this.treatments = treatments; } + @NonNull @Override public TreatmentsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.treatments_bolus_item, viewGroup, false); @@ -72,16 +68,16 @@ public class TreatmentsBolusFragment extends Fragment implements View.OnClickLis } @Override - public void onBindViewHolder(TreatmentsViewHolder holder, int position) { + public void onBindViewHolder(@NonNull TreatmentsViewHolder holder, int position) { Profile profile = ProfileFunctions.getInstance().getProfile(); if (profile == null) return; Treatment t = treatments.get(position); holder.date.setText(DateUtil.dateAndTimeString(t.date)); - holder.insulin.setText(DecimalFormatter.toPumpSupportedBolus(t.insulin) + " U"); - holder.carbs.setText(DecimalFormatter.to0Decimal(t.carbs) + " g"); + holder.insulin.setText(MainApp.gs(R.string.formatinsulinunits, t.insulin)); + holder.carbs.setText(MainApp.gs(R.string.format_carbs, (int) t.carbs)); Iob iob = t.iobCalc(System.currentTimeMillis(), profile.getDia()); - holder.iob.setText(DecimalFormatter.to2Decimal(iob.iobContrib) + " U"); + holder.iob.setText(MainApp.gs(R.string.formatinsulinunits, iob.iobContrib)); holder.mealOrCorrection.setText(t.isSMB ? "SMB" : t.mealBolus ? MainApp.gs(R.string.mealbolus) : MainApp.gs(R.string.correctionbous)); holder.ph.setVisibility(t.source == Source.PUMP ? View.VISIBLE : View.GONE); holder.ns.setVisibility(NSUpload.isIdValid(t._id) ? View.VISIBLE : View.GONE); @@ -105,7 +101,7 @@ public class TreatmentsBolusFragment extends Fragment implements View.OnClickLis } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); } @@ -124,19 +120,19 @@ public class TreatmentsBolusFragment extends Fragment implements View.OnClickLis TreatmentsViewHolder(View itemView) { super(itemView); - cv = (CardView) itemView.findViewById(R.id.treatments_cardview); - date = (TextView) itemView.findViewById(R.id.treatments_date); - insulin = (TextView) itemView.findViewById(R.id.treatments_insulin); - carbs = (TextView) itemView.findViewById(R.id.treatments_carbs); - iob = (TextView) itemView.findViewById(R.id.treatments_iob); - mealOrCorrection = (TextView) itemView.findViewById(R.id.treatments_mealorcorrection); - ph = (TextView) itemView.findViewById(R.id.pump_sign); - ns = (TextView) itemView.findViewById(R.id.ns_sign); - invalid = (TextView) itemView.findViewById(R.id.invalid_sign); - calculation = (TextView) itemView.findViewById(R.id.treatments_calculation); + cv = itemView.findViewById(R.id.treatments_cardview); + date = itemView.findViewById(R.id.treatments_date); + insulin = itemView.findViewById(R.id.treatments_insulin); + carbs = itemView.findViewById(R.id.treatments_carbs); + iob = itemView.findViewById(R.id.treatments_iob); + mealOrCorrection = itemView.findViewById(R.id.treatments_mealorcorrection); + ph = itemView.findViewById(R.id.pump_sign); + ns = itemView.findViewById(R.id.ns_sign); + invalid = itemView.findViewById(R.id.invalid_sign); + calculation = itemView.findViewById(R.id.treatments_calculation); calculation.setOnClickListener(this); calculation.setPaintFlags(calculation.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - remove = (TextView) itemView.findViewById(R.id.treatments_remove); + remove = itemView.findViewById(R.id.treatments_remove); remove.setOnClickListener(this); remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); } @@ -148,28 +144,25 @@ public class TreatmentsBolusFragment extends Fragment implements View.OnClickLis return; switch (v.getId()) { case R.id.treatments_remove: - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(treatment.date)); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - final String _id = treatment._id; - if (treatment.source == Source.PUMP) { - treatment.isValid = false; - TreatmentsPlugin.getPlugin().getService().update(treatment); - } else { - if (NSUpload.isIdValid(_id)) { - NSUpload.removeCareportalEntryFromNS(_id); + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.removerecord), + MainApp.gs(R.string.configbuilder_insulin) + ": " + MainApp.gs(R.string.formatinsulinunits, treatment.insulin) + + "\n" + MainApp.gs(R.string.carbs) + ": " + MainApp.gs(R.string.format_carbs, (int) treatment.carbs) + + "\n" + MainApp.gs(R.string.date) + ": " + DateUtil.dateAndTimeString(treatment.date), + (dialog, id) -> { + final String _id = treatment._id; + if (treatment.source == Source.PUMP) { + treatment.isValid = false; + TreatmentsPlugin.getPlugin().getService().update(treatment); } else { - UploadQueue.removeID("dbAdd", _id); + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + TreatmentsPlugin.getPlugin().getService().delete(treatment); } - TreatmentsPlugin.getPlugin().getService().delete(treatment); - } - updateGui(); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); + updateGui(); + }, null); break; case R.id.treatments_calculation: FragmentManager manager = getFragmentManager(); @@ -193,71 +186,48 @@ public class TreatmentsBolusFragment extends Fragment implements View.OnClickLis Bundle savedInstanceState) { View view = inflater.inflate(R.layout.treatments_bolus_fragment, container, false); - recyclerView = (RecyclerView) view.findViewById(R.id.treatments_recyclerview); + recyclerView = view.findViewById(R.id.treatments_recyclerview); recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(view.getContext()); + LinearLayoutManager llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); RecyclerViewAdapter adapter = new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTreatmentsFromHistory()); recyclerView.setAdapter(adapter); - iobTotal = (TextView) view.findViewById(R.id.treatments_iobtotal); - activityTotal = (TextView) view.findViewById(R.id.treatments_iobactivitytotal); + iobTotal = view.findViewById(R.id.treatments_iobtotal); + activityTotal = view.findViewById(R.id.treatments_iobactivitytotal); - refreshFromNS = (Button) view.findViewById(R.id.treatments_reshreshfromnightscout); - refreshFromNS.setOnClickListener(this); + Button refreshFromNS = view.findViewById(R.id.treatments_reshreshfromnightscout); + refreshFromNS.setOnClickListener(v -> OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.refresheventsfromnightscout) + "?", () -> { + TreatmentsPlugin.getPlugin().getService().resetTreatments(); + RxBus.INSTANCE.send(new EventNSClientRestart()); + })); - deleteFutureTreatments = (Button) view.findViewById(R.id.treatments_delete_future_treatments); - deleteFutureTreatments.setOnClickListener(this); + deleteFutureTreatments = view.findViewById(R.id.treatments_delete_future_treatments); + deleteFutureTreatments.setOnClickListener(v -> { + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.overview_treatment_label), MainApp.gs(R.string.deletefuturetreatments) + "?", () -> { + final List futureTreatments = TreatmentsPlugin.getPlugin().getService() + .getTreatmentDataFromTime(now() + 1000, true); + for (Treatment treatment : futureTreatments) { + final String _id = treatment._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + TreatmentsPlugin.getPlugin().getService().delete(treatment); + } + updateGui(); + }); + }); - boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); + boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, true); if (nsUploadOnly) refreshFromNS.setVisibility(View.GONE); - context = getContext(); - return view; } - @Override - public void onClick(View view) { - AlertDialog.Builder builder; - switch (view.getId()) { - case R.id.treatments_reshreshfromnightscout: - builder = new AlertDialog.Builder(this.getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.refresheventsfromnightscout) + "?"); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { - TreatmentsPlugin.getPlugin().getService().resetTreatments(); - RxBus.INSTANCE.send(new EventNSClientRestart()); - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - case R.id.treatments_delete_future_treatments: - builder = new AlertDialog.Builder(this.getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.deletefuturetreatments) + "?"); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { - final List futureTreatments = TreatmentsPlugin.getPlugin().getService() - .getTreatmentDataFromTime(now() + 1000, true); - for (Treatment treatment : futureTreatments) { - final String _id = treatment._id; - if (NSUpload.isIdValid(_id)) { - NSUpload.removeCareportalEntryFromNS(_id); - } else { - UploadQueue.removeID("dbAdd", _id); - } - TreatmentsPlugin.getPlugin().getService().delete(treatment); - } - updateGui(); - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - } - } - @Override public synchronized void onResume() { super.onResume(); @@ -292,5 +262,4 @@ public class TreatmentsBolusFragment extends Fragment implements View.OnClickLis deleteFutureTreatments.setVisibility(View.GONE); } } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.java index 4810a1ed8c..11b0c3a105 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.java @@ -1,7 +1,5 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.content.Context; -import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,7 +8,7 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; +import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; @@ -28,6 +26,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.Translator; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -37,14 +36,10 @@ import io.reactivex.disposables.CompositeDisposable; * Created by mike on 13/01/17. */ -public class TreatmentsCareportalFragment extends Fragment implements View.OnClickListener { +public class TreatmentsCareportalFragment extends Fragment { private CompositeDisposable disposable = new CompositeDisposable(); - RecyclerView recyclerView; - LinearLayoutManager llm; - Button refreshFromNS; - - Context context; + private RecyclerView recyclerView; public class RecyclerViewAdapter extends RecyclerView.Adapter { @@ -54,11 +49,11 @@ public class TreatmentsCareportalFragment extends Fragment implements View.OnCli this.careportalEventList = careportalEventList; } + @NonNull @Override public CareportalEventsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.treatments_careportal_item, viewGroup, false); - CareportalEventsViewHolder CareportalEventsViewHolder = new CareportalEventsViewHolder(v); - return CareportalEventsViewHolder; + return new CareportalEventsViewHolder(v); } @Override @@ -77,11 +72,11 @@ public class TreatmentsCareportalFragment extends Fragment implements View.OnCli } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); } - public class CareportalEventsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + class CareportalEventsViewHolder extends RecyclerView.ViewHolder { CardView cv; TextView date; TextView type; @@ -91,38 +86,30 @@ public class TreatmentsCareportalFragment extends Fragment implements View.OnCli CareportalEventsViewHolder(View itemView) { super(itemView); - cv = (CardView) itemView.findViewById(R.id.careportal_cardview); - date = (TextView) itemView.findViewById(R.id.careportal_date); - type = (TextView) itemView.findViewById(R.id.careportal_type); - note = (TextView) itemView.findViewById(R.id.careportal_note); - ns = (TextView) itemView.findViewById(R.id.ns_sign); - remove = (TextView) itemView.findViewById(R.id.careportal_remove); - remove.setOnClickListener(this); + cv = itemView.findViewById(R.id.careportal_cardview); + date = itemView.findViewById(R.id.careportal_date); + type = itemView.findViewById(R.id.careportal_type); + note = itemView.findViewById(R.id.careportal_note); + ns = itemView.findViewById(R.id.ns_sign); + remove = itemView.findViewById(R.id.careportal_remove); + remove.setOnClickListener(v -> { + final CareportalEvent careportalEvent = (CareportalEvent) v.getTag(); + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.removerecord), + "\n" + MainApp.gs(R.string.careportal_newnstreatment_eventtype) + ": " + Translator.translate(careportalEvent.eventType) + + "\n" + MainApp.gs(R.string.careportal_newnstreatment_notes_label) + ": " + careportalEvent.getNotes() + + "\n" + MainApp.gs(R.string.date) + ": " + DateUtil.dateAndTimeString(careportalEvent.date), + (dialog, id) -> { + final String _id = careportalEvent._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + MainApp.getDbHelper().delete(careportalEvent); + }, null); + }); remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); } - - @Override - public void onClick(View v) { - final CareportalEvent careportalEvent = (CareportalEvent) v.getTag(); - switch (v.getId()) { - case R.id.careportal_remove: - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(careportalEvent.date)); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { - final String _id = careportalEvent._id; - if (NSUpload.isIdValid(_id)) { - NSUpload.removeCareportalEntryFromNS(_id); - } else { - UploadQueue.removeID("dbAdd", _id); - } - MainApp.getDbHelper().delete(careportalEvent); - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - } - } } } @@ -131,22 +118,25 @@ public class TreatmentsCareportalFragment extends Fragment implements View.OnCli Bundle savedInstanceState) { View view = inflater.inflate(R.layout.treatments_careportal_fragment, container, false); - recyclerView = (RecyclerView) view.findViewById(R.id.careportal_recyclerview); + recyclerView = view.findViewById(R.id.careportal_recyclerview); recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(view.getContext()); + LinearLayoutManager llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEvents(false)); recyclerView.setAdapter(adapter); - refreshFromNS = (Button) view.findViewById(R.id.careportal_refreshfromnightscout); - refreshFromNS.setOnClickListener(this); + Button refreshFromNS = view.findViewById(R.id.careportal_refreshfromnightscout); + refreshFromNS.setOnClickListener(v -> + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.careportal), MainApp.gs(R.string.refresheventsfromnightscout) + " ?", () -> { + MainApp.getDbHelper().resetCareportalEvents(); + RxBus.INSTANCE.send(new EventNSClientRestart()); + })); - view.findViewById(R.id.careportal_removeandroidapsstartedevents).setOnClickListener(this); + view.findViewById(R.id.careportal_removeandroidapsstartedevents).setOnClickListener(v -> + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.careportal), MainApp.gs(R.string.careportal_removestartedevents), this::removeAndroidAPSStatedEvents)); - context = getContext(); - - boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); + boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, true); if (nsUploadOnly) refreshFromNS.setVisibility(View.GONE); @@ -170,36 +160,6 @@ public class TreatmentsCareportalFragment extends Fragment implements View.OnCli disposable.clear(); } - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.careportal_refreshfromnightscout: - AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.refresheventsfromnightscout) + " ?"); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - MainApp.getDbHelper().resetCareportalEvents(); - RxBus.INSTANCE.send(new EventNSClientRestart()); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - case R.id.careportal_removeandroidapsstartedevents: - builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.careportal_removestartedevents)); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { - removeAndroidAPSStatedEvents(); - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - } - - } - private void updateGui() { recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEvents(false)), false); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.java index 5ce7b57564..928e2d30aa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.java @@ -1,7 +1,5 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.content.Context; -import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; import android.view.LayoutInflater; @@ -9,7 +7,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; +import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -31,6 +29,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.OKDialog; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -38,10 +37,7 @@ import io.reactivex.disposables.CompositeDisposable; public class TreatmentsExtendedBolusesFragment extends Fragment { private CompositeDisposable disposable = new CompositeDisposable(); - RecyclerView recyclerView; - LinearLayoutManager llm; - - Context context; + private RecyclerView recyclerView; public class RecyclerViewAdapter extends RecyclerView.Adapter { @@ -51,6 +47,7 @@ public class TreatmentsExtendedBolusesFragment extends Fragment { this.extendedBolusList = extendedBolusList; } + @NonNull @Override public ExtendedBolusesViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.treatments_extendedbolus_item, viewGroup, false); @@ -101,11 +98,11 @@ public class TreatmentsExtendedBolusesFragment extends Fragment { } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); } - public class ExtendedBolusesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + class ExtendedBolusesViewHolder extends RecyclerView.ViewHolder { CardView cv; TextView date; TextView duration; @@ -120,31 +117,22 @@ public class TreatmentsExtendedBolusesFragment extends Fragment { ExtendedBolusesViewHolder(View itemView) { super(itemView); - cv = (CardView) itemView.findViewById(R.id.extendedboluses_cardview); - date = (TextView) itemView.findViewById(R.id.extendedboluses_date); - duration = (TextView) itemView.findViewById(R.id.extendedboluses_duration); - insulin = (TextView) itemView.findViewById(R.id.extendedboluses_insulin); - realDuration = (TextView) itemView.findViewById(R.id.extendedboluses_realduration); - ratio = (TextView) itemView.findViewById(R.id.extendedboluses_ratio); - insulinSoFar = (TextView) itemView.findViewById(R.id.extendedboluses_netinsulin); - iob = (TextView) itemView.findViewById(R.id.extendedboluses_iob); - ph = (TextView) itemView.findViewById(R.id.pump_sign); - ns = (TextView) itemView.findViewById(R.id.ns_sign); - remove = (TextView) itemView.findViewById(R.id.extendedboluses_remove); - remove.setOnClickListener(this); - remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - } - - @Override - public void onClick(View v) { - final ExtendedBolus extendedBolus = (ExtendedBolus) v.getTag(); - switch (v.getId()) { - case R.id.extendedboluses_remove: - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(extendedBolus.date)); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { + cv = itemView.findViewById(R.id.extendedboluses_cardview); + date = itemView.findViewById(R.id.extendedboluses_date); + duration = itemView.findViewById(R.id.extendedboluses_duration); + insulin = itemView.findViewById(R.id.extendedboluses_insulin); + realDuration = itemView.findViewById(R.id.extendedboluses_realduration); + ratio = itemView.findViewById(R.id.extendedboluses_ratio); + insulinSoFar = itemView.findViewById(R.id.extendedboluses_netinsulin); + iob = itemView.findViewById(R.id.extendedboluses_iob); + ph = itemView.findViewById(R.id.pump_sign); + ns = itemView.findViewById(R.id.ns_sign); + remove = itemView.findViewById(R.id.extendedboluses_remove); + remove.setOnClickListener(v -> { + final ExtendedBolus extendedBolus = (ExtendedBolus) v.getTag(); + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.removerecord), + MainApp.gs(R.string.extended_bolus) + + "\n" + MainApp.gs(R.string.date) + ": " + DateUtil.dateAndTimeString(extendedBolus.date), (dialog, id) -> { final String _id = extendedBolus._id; if (NSUpload.isIdValid(_id)) { NSUpload.removeCareportalEntryFromNS(_id); @@ -152,12 +140,9 @@ public class TreatmentsExtendedBolusesFragment extends Fragment { UploadQueue.removeID("dbAdd", _id); } MainApp.getDbHelper().delete(extendedBolus); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - } + }, null); + }); + remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); } } } @@ -167,16 +152,14 @@ public class TreatmentsExtendedBolusesFragment extends Fragment { Bundle savedInstanceState) { View view = inflater.inflate(R.layout.treatments_extendedbolus_fragment, container, false); - recyclerView = (RecyclerView) view.findViewById(R.id.extendedboluses_recyclerview); + recyclerView = view.findViewById(R.id.extendedboluses_recyclerview); recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(view.getContext()); + LinearLayoutManager llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); RecyclerViewAdapter adapter = new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getExtendedBolusesFromHistory()); recyclerView.setAdapter(adapter); - context = getContext(); - return view; } @@ -205,5 +188,4 @@ public class TreatmentsExtendedBolusesFragment extends Fragment { private void updateGui() { recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getExtendedBolusesFromHistory()), false); } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.java deleted file mode 100644 index 2d06b2824f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.java +++ /dev/null @@ -1,242 +0,0 @@ -package info.nightscout.androidaps.plugins.treatments.fragments; - -import android.content.Context; -import android.content.DialogInterface; -import android.graphics.Paint; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -import androidx.appcompat.app.AlertDialog; -import androidx.cardview.widget.CardView; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.ProfileSwitch; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.events.EventProfileNeedsUpdate; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.SP; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -/** - * Created by mike on 13/01/17. - */ - -public class TreatmentsProfileSwitchFragment extends Fragment implements View.OnClickListener { - private Logger log = LoggerFactory.getLogger(L.UI); - private CompositeDisposable disposable = new CompositeDisposable(); - - RecyclerView recyclerView; - LinearLayoutManager llm; - Button refreshFromNS; - - Context context; - - public class RecyclerViewAdapter extends RecyclerView.Adapter { - - List profileSwitchList; - - RecyclerViewAdapter(List profileSwitchList) { - this.profileSwitchList = profileSwitchList; - } - - @Override - public ProfileSwitchViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.treatments_profileswitch_item, viewGroup, false); - return new ProfileSwitchViewHolder(v); - } - - @Override - public void onBindViewHolder(ProfileSwitchViewHolder holder, int position) { - Profile profile = ProfileFunctions.getInstance().getProfile(); - if (profile == null) return; - ProfileSwitch profileSwitch = profileSwitchList.get(position); - holder.ph.setVisibility(profileSwitch.source == Source.PUMP ? View.VISIBLE : View.GONE); - holder.ns.setVisibility(NSUpload.isIdValid(profileSwitch._id) ? View.VISIBLE : View.GONE); - - holder.date.setText(DateUtil.dateAndTimeString(profileSwitch.date)); - if (!profileSwitch.isEndingEvent()) { - holder.duration.setText(DecimalFormatter.to0Decimal(profileSwitch.durationInMinutes) + " min"); - } else { - holder.duration.setText(""); - } - holder.name.setText(profileSwitch.getCustomizedName()); - if (profileSwitch.isInProgress()) - holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorActive)); - else - holder.date.setTextColor(holder.duration.getCurrentTextColor()); - holder.remove.setTag(profileSwitch); - holder.name.setTag(profileSwitch); - holder.date.setTag(profileSwitch); - holder.invalid.setVisibility(profileSwitch.isValid() ? View.GONE : View.VISIBLE); - - } - - @Override - public int getItemCount() { - return profileSwitchList.size(); - } - - @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - } - - public class ProfileSwitchViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - CardView cv; - TextView date; - TextView duration; - TextView name; - TextView remove; - TextView ph; - TextView ns; - TextView invalid; - - ProfileSwitchViewHolder(View itemView) { - super(itemView); - cv = (CardView) itemView.findViewById(R.id.profileswitch_cardview); - date = (TextView) itemView.findViewById(R.id.profileswitch_date); - duration = (TextView) itemView.findViewById(R.id.profileswitch_duration); - name = (TextView) itemView.findViewById(R.id.profileswitch_name); - ph = (TextView) itemView.findViewById(R.id.pump_sign); - ns = (TextView) itemView.findViewById(R.id.ns_sign); - invalid = (TextView) itemView.findViewById(R.id.invalid_sign); - remove = (TextView) itemView.findViewById(R.id.profileswitch_remove); - remove.setOnClickListener(this); - remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - name.setOnClickListener(this); - date.setOnClickListener(this); - - } - - @Override - public void onClick(View v) { - final ProfileSwitch profileSwitch = (ProfileSwitch) v.getTag(); - if (profileSwitch == null) { - log.error("profileSwitch == null"); - return; - } - switch (v.getId()) { - case R.id.profileswitch_remove: - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(profileSwitch.date)); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - final String _id = profileSwitch._id; - if (NSUpload.isIdValid(_id)) { - NSUpload.removeCareportalEntryFromNS(_id); - } else { - UploadQueue.removeID("dbAdd", _id); - } - MainApp.getDbHelper().delete(profileSwitch); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - case R.id.profileswitch_date: - case R.id.profileswitch_name: - Bundle args = new Bundle(); - args.putLong("time", ((ProfileSwitch) v.getTag()).date); - args.putLong("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal()); - ProfileViewerDialog pvd = new ProfileViewerDialog(); - pvd.setArguments(args); - FragmentManager manager = getFragmentManager(); - if (manager != null) - pvd.show(manager, "ProfileViewDialog"); - break; - } - } - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.treatments_profileswitch_fragment, container, false); - - recyclerView = (RecyclerView) view.findViewById(R.id.profileswitch_recyclerview); - recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(view.getContext()); - recyclerView.setLayoutManager(llm); - - RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().getProfileSwitchData(false)); - recyclerView.setAdapter(adapter); - - refreshFromNS = (Button) view.findViewById(R.id.profileswitch_refreshfromnightscout); - refreshFromNS.setOnClickListener(this); - - context = getContext(); - - boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); - if (nsUploadOnly) - refreshFromNS.setVisibility(View.GONE); - - return view; - } - - @Override - public synchronized void onResume() { - super.onResume(); - disposable.add(RxBus.INSTANCE - .toObservable(EventProfileNeedsUpdate.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> updateGUI(), FabricPrivacy::logException) - ); - updateGUI(); - } - - @Override - public synchronized void onPause() { - super.onPause(); - disposable.clear(); - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.profileswitch_refreshfromnightscout: - AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.refresheventsfromnightscout) + "?"); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - MainApp.getDbHelper().resetProfileSwitch(); - RxBus.INSTANCE.send(new EventNSClientRestart()); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - } - } - - protected void updateGUI() { - recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getProfileSwitchData(false)), false); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt new file mode 100644 index 0000000000..c2b16f5d13 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt @@ -0,0 +1,163 @@ +package info.nightscout.androidaps.plugins.treatments.fragments + +import android.graphics.Paint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.cardview.widget.CardView +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.db.ProfileSwitch +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.dialogs.ProfileViewerDialog +import info.nightscout.androidaps.events.EventProfileNeedsUpdate +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart +import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin +import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged +import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder +import info.nightscout.androidaps.utils.* +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.treatments_profileswitch_fragment.* + +class TreatmentsProfileSwitchFragment : Fragment() { + private val disposable = CompositeDisposable() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.treatments_profileswitch_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + profileswitch_recyclerview.setHasFixedSize(true) + profileswitch_recyclerview.layoutManager = LinearLayoutManager(view.context) + profileswitch_recyclerview.adapter = RecyclerProfileViewAdapter(MainApp.getDbHelper().getProfileSwitchData(DateUtil.now() - T.days(30).msecs(), false)) + + profileswitch_refreshfromnightscout.setOnClickListener { + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.refresheventsfromnightscout) + "?", Runnable { + MainApp.getDbHelper().resetProfileSwitch() + RxBus.send(EventNSClientRestart()) + }) + } + } + if (SP.getBoolean(R.string.key_ns_upload_only, true)) profileswitch_refreshfromnightscout.visibility = View.GONE + + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable.add(RxBus + .toObservable(EventProfileNeedsUpdate::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }) { FabricPrivacy.logException(it) } + ) + updateGUI() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + fun updateGUI() = + profileswitch_recyclerview?.swapAdapter(RecyclerProfileViewAdapter(MainApp.getDbHelper().getProfileSwitchData(DateUtil.now() - T.days(30).msecs(), false)), false) + + inner class RecyclerProfileViewAdapter(private var profileSwitchList: List) : RecyclerView.Adapter() { + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ProfileSwitchViewHolder { + return ProfileSwitchViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_profileswitch_item, viewGroup, false)) + } + + override fun onBindViewHolder(holder: ProfileSwitchViewHolder, position: Int) { + val profileSwitch = profileSwitchList[position] + holder.ph.visibility = (profileSwitch.source == Source.PUMP).toVisibility() + holder.ns.visibility = NSUpload.isIdValid(profileSwitch._id).toVisibility() + holder.date.text = DateUtil.dateAndTimeString(profileSwitch.date) + if (!profileSwitch.isEndingEvent) { + holder.duration.text = DecimalFormatter.to0Decimal(profileSwitch.durationInMinutes.toDouble()) + " " + MainApp.gs(R.string.unit_minute_short) + } else { + holder.duration.text = "" + } + holder.name.text = profileSwitch.customizedName + if (profileSwitch.isInProgress) holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorActive)) else holder.date.setTextColor(holder.duration.currentTextColor) + holder.remove.tag = profileSwitch + holder.clone.tag = profileSwitch + holder.name.tag = profileSwitch + holder.date.tag = profileSwitch + holder.invalid.visibility = if (profileSwitch.isValid()) View.GONE else View.VISIBLE + } + + override fun getItemCount(): Int { + return profileSwitchList.size + } + + inner class ProfileSwitchViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { + var cv: CardView = itemView.findViewById(R.id.profileswitch_cardview) as CardView + var date: TextView = itemView.findViewById(R.id.profileswitch_date) as TextView + var duration: TextView = itemView.findViewById(R.id.profileswitch_duration) as TextView + var name: TextView = itemView.findViewById(R.id.profileswitch_name) as TextView + var remove: TextView = itemView.findViewById(R.id.profileswitch_remove) as TextView + var clone: TextView = itemView.findViewById(R.id.profileswitch_clone) as TextView + var ph: TextView = itemView.findViewById(R.id.pump_sign) as TextView + var ns: TextView = itemView.findViewById(R.id.ns_sign) as TextView + var invalid: TextView = itemView.findViewById(R.id.invalid_sign) as TextView + + override fun onClick(v: View) { + val profileSwitch = v.tag as ProfileSwitch + when (v.id) { + R.id.profileswitch_remove -> + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.removerecord), + MainApp.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName + + "\n" + MainApp.gs(R.string.date) + ": " + DateUtil.dateAndTimeString(profileSwitch.date), Runnable { + val id = profileSwitch._id + if (NSUpload.isIdValid(id)) NSUpload.removeCareportalEntryFromNS(id) + else UploadQueue.removeID("dbAdd", id) + MainApp.getDbHelper().delete(profileSwitch) + }) + } + R.id.profileswitch_clone -> + activity?.let { activity -> + OKDialog.showConfirmation(activity, MainApp.gs(R.string.careportal_profileswitch), MainApp.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.customizedName + "\n" + DateUtil.dateAndTimeString(profileSwitch.date), Runnable { + profileSwitch.profileObject?.let { + val nonCustomized = it.convertToNonCustomizedProfile() + LocalProfilePlugin.addProfile(LocalProfilePlugin.SingleProfile().copyFrom(nonCustomized, profileSwitch.customizedName + " " + DateUtil.dateAndTimeString(profileSwitch.date).replace(".", "_"))) + RxBus.send(EventLocalProfileChanged()) + } + }) + } + + R.id.profileswitch_date, R.id.profileswitch_name -> { + val args = Bundle() + args.putLong("time", (v.tag as ProfileSwitch).date) + args.putInt("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal) + val pvd = ProfileViewerDialog() + pvd.arguments = args + fragmentManager?.let { pvd.show(it, "ProfileViewDialog") } + } + } + } + + init { + remove.setOnClickListener(this) + clone.setOnClickListener(this) + remove.paintFlags = remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + clone.paintFlags = clone.paintFlags or Paint.UNDERLINE_TEXT_FLAG + name.setOnClickListener(this) + date.setOnClickListener(this) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.java index ccd1b327c9..f42f59a527 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.java @@ -1,8 +1,5 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; import android.view.LayoutInflater; @@ -11,7 +8,7 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; +import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -33,6 +30,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -41,14 +39,10 @@ import io.reactivex.disposables.CompositeDisposable; * Created by mike on 13/01/17. */ -public class TreatmentsTempTargetFragment extends Fragment implements View.OnClickListener { +public class TreatmentsTempTargetFragment extends Fragment { private CompositeDisposable disposable = new CompositeDisposable(); - RecyclerView recyclerView; - LinearLayoutManager llm; - Button refreshFromNS; - - Context context; + private RecyclerView recyclerView; public class RecyclerViewAdapter extends RecyclerView.Adapter { @@ -60,16 +54,16 @@ public class TreatmentsTempTargetFragment extends Fragment implements View.OnCli currentlyActiveTarget = tempTargetList.getValueByInterval(System.currentTimeMillis()); } + @NonNull @Override public TempTargetsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.treatments_temptarget_item, viewGroup, false); - TempTargetsViewHolder TempTargetsViewHolder = new TempTargetsViewHolder(v); - return TempTargetsViewHolder; + return new TempTargetsViewHolder(v); } @Override public void onBindViewHolder(TempTargetsViewHolder holder, int position) { - String units = ProfileFunctions.getInstance().getProfileUnits(); + String units = ProfileFunctions.getSystemUnits(); TempTarget tempTarget = tempTargetList.getReversed(position); holder.ph.setVisibility(tempTarget.source == Source.PUMP ? View.VISIBLE : View.GONE); holder.ns.setVisibility(NSUpload.isIdValid(tempTarget._id) ? View.VISIBLE : View.GONE); @@ -104,11 +98,11 @@ public class TreatmentsTempTargetFragment extends Fragment implements View.OnCli } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); } - public class TempTargetsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + class TempTargetsViewHolder extends RecyclerView.ViewHolder { CardView cv; TextView date; TextView duration; @@ -123,31 +117,23 @@ public class TreatmentsTempTargetFragment extends Fragment implements View.OnCli TempTargetsViewHolder(View itemView) { super(itemView); - cv = (CardView) itemView.findViewById(R.id.temptargetrange_cardview); - date = (TextView) itemView.findViewById(R.id.temptargetrange_date); - duration = (TextView) itemView.findViewById(R.id.temptargetrange_duration); - low = (TextView) itemView.findViewById(R.id.temptargetrange_low); - high = (TextView) itemView.findViewById(R.id.temptargetrange_high); - reason = (TextView) itemView.findViewById(R.id.temptargetrange_reason); - reasonLabel = (TextView) itemView.findViewById(R.id.temptargetrange_reason_label); - reasonColon = (TextView) itemView.findViewById(R.id.temptargetrange_reason_colon); - ph = (TextView) itemView.findViewById(R.id.pump_sign); - ns = (TextView) itemView.findViewById(R.id.ns_sign); - remove = (TextView) itemView.findViewById(R.id.temptargetrange_remove); - remove.setOnClickListener(this); - remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - } - - @Override - public void onClick(View v) { - final TempTarget tempTarget = (TempTarget) v.getTag(); - switch (v.getId()) { - case R.id.temptargetrange_remove: - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(tempTarget.date)); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { + cv = itemView.findViewById(R.id.temptargetrange_cardview); + date = itemView.findViewById(R.id.temptargetrange_date); + duration = itemView.findViewById(R.id.temptargetrange_duration); + low = itemView.findViewById(R.id.temptargetrange_low); + high = itemView.findViewById(R.id.temptargetrange_high); + reason = itemView.findViewById(R.id.temptargetrange_reason); + reasonLabel = itemView.findViewById(R.id.temptargetrange_reason_label); + reasonColon = itemView.findViewById(R.id.temptargetrange_reason_colon); + ph = itemView.findViewById(R.id.pump_sign); + ns = itemView.findViewById(R.id.ns_sign); + remove = itemView.findViewById(R.id.temptargetrange_remove); + remove.setOnClickListener(v -> { + final TempTarget tempTarget = (TempTarget) v.getTag(); + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.removerecord), + MainApp.gs(R.string.careportal_temporarytarget) + ": " + tempTarget.friendlyDescription(ProfileFunctions.getSystemUnits()) + + "\n" + DateUtil.dateAndTimeString(tempTarget.date), + (dialog, id) -> { final String _id = tempTarget._id; if (NSUpload.isIdValid(_id)) { NSUpload.removeCareportalEntryFromNS(_id); @@ -155,12 +141,9 @@ public class TreatmentsTempTargetFragment extends Fragment implements View.OnCli UploadQueue.removeID("dbAdd", _id); } MainApp.getDbHelper().delete(tempTarget); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - } + }, null); + }); + remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); } } } @@ -170,20 +153,22 @@ public class TreatmentsTempTargetFragment extends Fragment implements View.OnCli Bundle savedInstanceState) { View view = inflater.inflate(R.layout.treatments_temptarget_fragment, container, false); - recyclerView = (RecyclerView) view.findViewById(R.id.temptargetrange_recyclerview); + recyclerView = view.findViewById(R.id.temptargetrange_recyclerview); recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(view.getContext()); + LinearLayoutManager llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); RecyclerViewAdapter adapter = new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTempTargetsFromHistory()); recyclerView.setAdapter(adapter); - refreshFromNS = (Button) view.findViewById(R.id.temptargetrange_refreshfromnightscout); - refreshFromNS.setOnClickListener(this); + Button refreshFromNS = view.findViewById(R.id.temptargetrange_refreshfromnightscout); + refreshFromNS.setOnClickListener(v -> + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.refresheventsfromnightscout) + " ?", () -> { + MainApp.getDbHelper().resetTempTargets(); + RxBus.INSTANCE.send(new EventNSClientRestart()); + })); - context = getContext(); - - boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); + boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, true); if (nsUploadOnly) refreshFromNS.setVisibility(View.GONE); @@ -207,26 +192,6 @@ public class TreatmentsTempTargetFragment extends Fragment implements View.OnCli disposable.clear(); } - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.temptargetrange_refreshfromnightscout: - AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext()); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.refresheventsfromnightscout) + " ?"); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - MainApp.getDbHelper().resetTempTargets(); - RxBus.INSTANCE.send(new EventNSClientRestart()); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - } - - } - private void updateGui() { recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTempTargetsFromHistory()), false); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.java index 45f930410e..22dd86e381 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.content.Context; import android.graphics.Paint; import android.os.Bundle; import android.view.LayoutInflater; @@ -9,7 +8,6 @@ import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.cardview.widget.CardView; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -33,6 +31,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.OKDialog; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -40,12 +39,9 @@ import io.reactivex.disposables.CompositeDisposable; public class TreatmentsTemporaryBasalsFragment extends Fragment { private CompositeDisposable disposable = new CompositeDisposable(); - RecyclerView recyclerView; - LinearLayoutManager llm; + private RecyclerView recyclerView; - TextView tempBasalTotalView; - - Context context; + private TextView tempBasalTotalView; public class RecyclerViewAdapter extends RecyclerView.Adapter { @@ -55,6 +51,7 @@ public class TreatmentsTemporaryBasalsFragment extends Fragment { this.tempBasalList = tempBasalList; } + @NonNull @Override public TempBasalsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.treatments_tempbasals_item, viewGroup, false); @@ -123,11 +120,11 @@ public class TreatmentsTemporaryBasalsFragment extends Fragment { } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); } - public class TempBasalsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + class TempBasalsViewHolder extends RecyclerView.ViewHolder { CardView cv; TextView date; TextView duration; @@ -144,45 +141,37 @@ public class TreatmentsTemporaryBasalsFragment extends Fragment { TempBasalsViewHolder(View itemView) { super(itemView); - cv = (CardView) itemView.findViewById(R.id.tempbasals_cardview); - date = (TextView) itemView.findViewById(R.id.tempbasals_date); - duration = (TextView) itemView.findViewById(R.id.tempbasals_duration); - absolute = (TextView) itemView.findViewById(R.id.tempbasals_absolute); - percent = (TextView) itemView.findViewById(R.id.tempbasals_percent); - realDuration = (TextView) itemView.findViewById(R.id.tempbasals_realduration); - netRatio = (TextView) itemView.findViewById(R.id.tempbasals_netratio); - netInsulin = (TextView) itemView.findViewById(R.id.tempbasals_netinsulin); - iob = (TextView) itemView.findViewById(R.id.tempbasals_iob); - extendedFlag = (TextView) itemView.findViewById(R.id.tempbasals_extendedflag); - ph = (TextView) itemView.findViewById(R.id.pump_sign); - ns = (TextView) itemView.findViewById(R.id.ns_sign); - remove = (TextView) itemView.findViewById(R.id.tempbasals_remove); - remove.setOnClickListener(this); + cv = itemView.findViewById(R.id.tempbasals_cardview); + date = itemView.findViewById(R.id.tempbasals_date); + duration = itemView.findViewById(R.id.tempbasals_duration); + absolute = itemView.findViewById(R.id.tempbasals_absolute); + percent = itemView.findViewById(R.id.tempbasals_percent); + realDuration = itemView.findViewById(R.id.tempbasals_realduration); + netRatio = itemView.findViewById(R.id.tempbasals_netratio); + netInsulin = itemView.findViewById(R.id.tempbasals_netinsulin); + iob = itemView.findViewById(R.id.tempbasals_iob); + extendedFlag = itemView.findViewById(R.id.tempbasals_extendedflag); + ph = itemView.findViewById(R.id.pump_sign); + ns = itemView.findViewById(R.id.ns_sign); + remove = itemView.findViewById(R.id.tempbasals_remove); + remove.setOnClickListener(v -> { + final TemporaryBasal tempBasal = (TemporaryBasal) v.getTag(); + OKDialog.showConfirmation(getContext(), MainApp.gs(R.string.removerecord), + MainApp.gs(R.string.pump_tempbasal_label) + ": " + tempBasal.toStringFull() + + "\n" + MainApp.gs(R.string.date) + ": " + DateUtil.dateAndTimeString(tempBasal.date), + ((dialog, id) -> { + final String _id = tempBasal._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + MainApp.getDbHelper().delete(tempBasal); + }), null); + }); remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); } - @Override - public void onClick(View v) { - final TemporaryBasal tempBasal = (TemporaryBasal) v.getTag(); - switch (v.getId()) { - case R.id.tempbasals_remove: - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(tempBasal.date)); - builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { - final String _id = tempBasal._id; - if (NSUpload.isIdValid(_id)) { - NSUpload.removeCareportalEntryFromNS(_id); - } else { - UploadQueue.removeID("dbAdd", _id); - } - MainApp.getDbHelper().delete(tempBasal); - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); - break; - } - } } } @@ -191,17 +180,15 @@ public class TreatmentsTemporaryBasalsFragment extends Fragment { Bundle savedInstanceState) { View view = inflater.inflate(R.layout.treatments_tempbasals_fragment, container, false); - recyclerView = (RecyclerView) view.findViewById(R.id.tempbasals_recyclerview); + recyclerView = view.findViewById(R.id.tempbasals_recyclerview); recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(view.getContext()); + LinearLayoutManager llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); RecyclerViewAdapter adapter = new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTemporaryBasalsFromHistory()); recyclerView.setAdapter(adapter); - tempBasalTotalView = (TextView) view.findViewById(R.id.tempbasals_totaltempiob); - - context = getContext(); + tempBasalTotalView = view.findViewById(R.id.tempbasals_totaltempiob); return view; } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java index f24b02171f..05efc63aa3 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java @@ -15,6 +15,7 @@ import java.util.LinkedList; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.BolusProgressHelperActivity; import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; @@ -25,8 +26,7 @@ import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; -import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressHelperActivity; +import info.nightscout.androidaps.dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; @@ -279,7 +279,7 @@ public class CommandQueue { } removeAll(Command.CommandType.BOLUS); removeAll(Command.CommandType.SMB_BOLUS); - ConfigBuilderPlugin.getPlugin().getActivePump().stopBolusDelivering(); + new Thread(() -> ConfigBuilderPlugin.getPlugin().getActivePump().stopBolusDelivering()).run(); } // returns true if command is queued diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java index 6348ea31b9..5a67759594 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java @@ -8,7 +8,7 @@ import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; +import info.nightscout.androidaps.dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.DecimalFormatter; diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java index 62e14285eb..b69f3f4dc7 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java @@ -50,7 +50,7 @@ public class CommandSetProfile extends Command { // Send SMS notification if ProfileSwitch is comming from NS ProfileSwitch profileSwitch = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(System.currentTimeMillis()); if (profileSwitch != null && r.enacted && profileSwitch.source == Source.NIGHTSCOUT) { - SmsCommunicatorPlugin smsCommunicatorPlugin = SmsCommunicatorPlugin.getPlugin(); + SmsCommunicatorPlugin smsCommunicatorPlugin = SmsCommunicatorPlugin.INSTANCE; if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { smsCommunicatorPlugin.sendNotificationToAllNumbers(MainApp.gs(R.string.profile_set_ok)); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/AutoStartReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.java similarity index 89% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/AutoStartReceiver.java rename to app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.java index 6c8c7ae159..8abfb22953 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/receivers/AutoStartReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.receivers; +package info.nightscout.androidaps.receivers; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java index 9af15e9ddc..cb49d8cf4e 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java @@ -21,7 +21,7 @@ public class ChargingStateReceiver extends BroadcastReceiver { lastEvent = event; } - public EventChargingState grabChargingState(Context context) { + public static EventChargingState grabChargingState(Context context) { BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE); if (bm == null) diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java deleted file mode 100644 index 4c8a3f660e..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java +++ /dev/null @@ -1,110 +0,0 @@ -package info.nightscout.androidaps.receivers; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.PowerManager; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.events.EventProfileNeedsUpdate; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.queue.commands.Command; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.LocalAlertUtils; -import info.nightscout.androidaps.utils.T; - - -/** - * Created by mike on 07.07.2016. - */ -public class KeepAliveReceiver extends BroadcastReceiver { - private static Logger log = LoggerFactory.getLogger(L.CORE); - public static final long STATUS_UPDATE_FREQUENCY = T.mins(15).msecs(); - private static long lastReadStatus = 0; - private static long lastRun = 0; - - public static void cancelAlarm(Context context) { - Intent intent = new Intent(context, KeepAliveReceiver.class); - PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0); - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - alarmManager.cancel(sender); - } - - @Override - public void onReceive(Context context, Intent rIntent) { - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:KeepAliveReciever"); - wl.acquire(); - - LocalAlertUtils.shortenSnoozeInterval(); - LocalAlertUtils.checkStaleBGAlert(); - checkPump(); - - if (L.isEnabled(L.CORE)) - log.debug("KeepAlive received"); - wl.release(); - } - - private void checkPump() { - final PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - final Profile profile = ProfileFunctions.getInstance().getProfile(); - if (pump != null && profile != null) { - long lastConnection = pump.lastDataTime(); - boolean isStatusOutdated = lastConnection + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis(); - boolean isBasalOutdated = Math.abs(profile.getBasal() - pump.getBaseBasalRate()) > pump.getPumpDescription().basalStep; - - if (L.isEnabled(L.CORE)) - log.debug("Last connection: " + DateUtil.dateAndTimeString(lastConnection)); - // sometimes keepalive broadcast stops - // as as workaround test if readStatus was requested before an alarm is generated - if (lastReadStatus != 0 && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) { - LocalAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated); - } - - if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getPlugin().getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { - RxBus.INSTANCE.send(new EventProfileNeedsUpdate()); - } else if (isStatusOutdated && !pump.isBusy()) { - lastReadStatus = System.currentTimeMillis(); - ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("KeepAlive. Status outdated.", null); - } else if (isBasalOutdated && !pump.isBusy()) { - lastReadStatus = System.currentTimeMillis(); - ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("KeepAlive. Basal outdated.", null); - } - } - if (lastRun != 0 && System.currentTimeMillis() - lastRun > T.mins(10).msecs()) { - log.error("KeepAlive fail"); - FabricPrivacy.getInstance().logCustom("KeepAliveFail"); - } - lastRun = System.currentTimeMillis(); - } - - //called by MainApp at first app start - public void setAlarm(Context context) { - - LocalAlertUtils.shortenSnoozeInterval(); - LocalAlertUtils.presnoozeAlarms(); - - AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - Intent i = new Intent(context, KeepAliveReceiver.class); - PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0); - try { - pi.send(); - } catch (PendingIntent.CanceledException e) { - } - am.cancel(pi); - am.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), Constants.keepAliveMsecs, pi); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt new file mode 100644 index 0000000000..7ca4f962ae --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt @@ -0,0 +1,121 @@ +package info.nightscout.androidaps.receivers + +import android.app.AlarmManager +import android.app.PendingIntent +import android.app.PendingIntent.CanceledException +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.PowerManager +import android.os.SystemClock +import info.nightscout.androidaps.events.EventProfileNeedsUpdate +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus.send +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.queue.commands.Command +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.LocalAlertUtils +import info.nightscout.androidaps.utils.T +import org.slf4j.LoggerFactory +import kotlin.math.abs + +class KeepAliveReceiver : BroadcastReceiver() { + private var lastReadStatus: Long = 0 + private var lastRun: Long = 0 + private var lastIobUpload: Long = 0 + + override fun onReceive(context: Context, rIntent: Intent) { + if (L.isEnabled(L.CORE)) + log.debug("KeepAlive received") + val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager + val wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:KeepAliveReceiver") + wl.acquire(T.mins(2).msecs()) + LocalAlertUtils.shortenSnoozeInterval() + LocalAlertUtils.checkStaleBGAlert() + checkPump() + checkAPS() + wl.release() + } + + companion object { + private val log = LoggerFactory.getLogger(L.CORE) + + private val KEEP_ALIVE_MILLISECONDS = T.mins(5).msecs() + private val STATUS_UPDATE_FREQUENCY = T.mins(15).msecs() + private val IOB_UPDATE_FREQUENCY = T.mins(5).msecs() + + //called by MainApp at first app start + @JvmStatic + fun setAlarm(context: Context) { + if (L.isEnabled(L.CORE)) + log.debug("KeepAlive scheduled") + SystemClock.sleep(5000) // wait for app initialization + LocalAlertUtils.shortenSnoozeInterval() + LocalAlertUtils.presnoozeAlarms() + val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + val i = Intent(context, KeepAliveReceiver::class.java) + val pi = PendingIntent.getBroadcast(context, 0, i, 0) + try { + pi.send() + } catch (e: CanceledException) { + } + am.cancel(pi) + am.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), KEEP_ALIVE_MILLISECONDS, pi) + } + + @JvmStatic + fun cancelAlarm(context: Context) { + if (L.isEnabled(L.CORE)) + log.debug("KeepAlive canceled") + val intent = Intent(context, KeepAliveReceiver::class.java) + val sender = PendingIntent.getBroadcast(context, 0, intent, 0) + val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + alarmManager.cancel(sender) + } + } + + private fun checkAPS() { + val usedAPS = ConfigBuilderPlugin.getPlugin().activeAPS + var shouldUploadStatus = false + if (usedAPS == null) shouldUploadStatus = true + else if (DateUtil.isOlderThan(usedAPS.lastAPSRun, 5)) shouldUploadStatus = true + if (DateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY) && shouldUploadStatus) { + lastIobUpload = DateUtil.now() + NSUpload.uploadDeviceStatus() + } + } + + private fun checkPump() { + val pump = ConfigBuilderPlugin.getPlugin().activePump + val profile = ProfileFunctions.getInstance().profile + if (pump != null && profile != null) { + val lastConnection = pump.lastDataTime() + val isStatusOutdated = lastConnection + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis() + val isBasalOutdated = abs(profile.basal - pump.baseBasalRate) > pump.pumpDescription.basalStep + if (L.isEnabled(L.CORE)) + log.debug("Last connection: " + DateUtil.dateAndTimeString(lastConnection)) + // sometimes keep alive broadcast stops + // as as workaround test if readStatus was requested before an alarm is generated + if (lastReadStatus != 0L && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) { + LocalAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated) + } + if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getPlugin().commandQueue.isRunning(Command.CommandType.BASALPROFILE)) { + send(EventProfileNeedsUpdate()) + } else if (isStatusOutdated && !pump.isBusy) { + lastReadStatus = System.currentTimeMillis() + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("KeepAlive. Status outdated.", null) + } else if (isBasalOutdated && !pump.isBusy) { + lastReadStatus = System.currentTimeMillis() + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("KeepAlive. Basal outdated.", null) + } + } + if (lastRun != 0L && System.currentTimeMillis() - lastRun > T.mins(10).msecs()) { + log.error("KeepAlive fail") + FabricPrivacy.getInstance().logCustom("KeepAliveFail") + } + lastRun = System.currentTimeMillis() + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java index 3f12d75fce..19cf12bc8a 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java @@ -26,11 +26,6 @@ public class NetworkChangeReceiver extends BroadcastReceiver { public static final NetworkChangeReceiver instance = new NetworkChangeReceiver(); - // TODO: Split NSClient into network state component that can be used by several plugins and logic for plugin - public static void fetch() { - new NetworkChangeReceiver().grabNetworkStatus(MainApp.instance().getApplicationContext()); - } - @Override public void onReceive(final Context context, final Intent intent) { EventNetworkChange event = grabNetworkStatus(context); @@ -39,7 +34,7 @@ public class NetworkChangeReceiver extends BroadcastReceiver { } @Nullable - public EventNetworkChange grabNetworkStatus(final Context context) { + public static EventNetworkChange grabNetworkStatus(final Context context) { EventNetworkChange event = new EventNetworkChange(); ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/app/src/main/java/info/nightscout/androidaps/services/DataService.java b/app/src/main/java/info/nightscout/androidaps/services/DataService.java index 80a42e145a..b31ef78e7d 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/services/DataService.java @@ -54,12 +54,8 @@ public class DataService extends IntentService { log.debug("onHandleIntent " + BundleLogger.log(intent.getExtras())); } - boolean acceptNSData = !SP.getBoolean(R.string.key_ns_upload_only, false); + boolean acceptNSData = !SP.getBoolean(R.string.key_ns_upload_only, true); Bundle bundles = intent.getExtras(); - if (bundles != null && bundles.containsKey("islocal")) { - acceptNSData = acceptNSData || bundles.getBoolean("islocal"); - } - final String action = intent.getAction(); if (Intents.ACTION_NEW_BG_ESTIMATE.equals(action)) { @@ -103,7 +99,7 @@ public class DataService extends IntentService { ) { handleNewDataFromNSClient(intent); } else if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action)) { - SmsCommunicatorPlugin.getPlugin().handleNewData(intent); + SmsCommunicatorPlugin.INSTANCE.handleNewData(intent); } if (L.isEnabled(L.DATASERVICE)) diff --git a/app/src/main/java/info/nightscout/androidaps/services/Intents.java b/app/src/main/java/info/nightscout/androidaps/services/Intents.java index 98c2355a4f..e4a2d0b33b 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/Intents.java +++ b/app/src/main/java/info/nightscout/androidaps/services/Intents.java @@ -20,11 +20,6 @@ public interface Intents { String ACTION_URGENT_ALARM = "info.nightscout.client.URGENT_ALARM"; String ACTION_CLEAR_ALARM = "info.nightscout.client.CLEAR_ALARM"; - - // App -> NSClient - String ACTION_DATABASE = "info.nightscout.client.DBACCESS"; - String ACTION_ACK_ALARM = "info.nightscout.client.ACK_ALARM"; - // xDrip -> App String RECEIVER_PERMISSION = "com.eveningoutpost.dexdrip.permissions.RECEIVE_BG_ESTIMATE"; diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java index 2c9e1ea51b..a0dff38104 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.List; import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.activities.PreferencesActivity; @@ -22,21 +23,18 @@ import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment; import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; -import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment; -import info.nightscout.androidaps.plugins.general.careportal.Dialogs.NewNSTreatmentDialog; -import info.nightscout.androidaps.plugins.general.careportal.OptionsToShow; import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs; import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus; import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService; +import info.nightscout.androidaps.dialogs.ProfileSwitchDialog; import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment; import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin; import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment; import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; -import info.nightscout.androidaps.plugins.profile.simple.SimpleProfileFragment; -import info.nightscout.androidaps.plugins.profile.simple.SimpleProfilePlugin; import info.nightscout.androidaps.setupwizard.elements.SWBreak; import info.nightscout.androidaps.setupwizard.elements.SWButton; +import info.nightscout.androidaps.setupwizard.elements.SWEditNumberWithUnits; import info.nightscout.androidaps.setupwizard.elements.SWEditString; import info.nightscout.androidaps.setupwizard.elements.SWEditUrl; import info.nightscout.androidaps.setupwizard.elements.SWFragment; @@ -50,6 +48,8 @@ import info.nightscout.androidaps.utils.LocaleHelper; import info.nightscout.androidaps.utils.PasswordProtection; import info.nightscout.androidaps.utils.SP; +import static info.nightscout.androidaps.utils.EspressoTestHelperKt.isRunningTest; + public class SWDefinition { private AppCompatActivity activity; private List screens = new ArrayList<>(); @@ -67,7 +67,7 @@ public class SWDefinition { } private SWDefinition add(SWScreen newScreen) { - screens.add(newScreen); + if (newScreen != null) screens.add(newScreen); return this; } @@ -110,6 +110,28 @@ public class SWDefinition { .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) .validator(() -> SP.getBoolean(R.string.key_i_understand, false)); + private SWScreen screenUnits = new SWScreen(R.string.units) + .skippable(false) + .add(new SWRadioButton() + .option(R.array.unitsArray, R.array.unitsValues) + .preferenceId(R.string.key_units).label(R.string.units) + .comment(R.string.setupwizard_units_prompt)) + .validator(() -> SP.contains(R.string.key_units)); + + private SWScreen displaySettings = new SWScreen(R.string.wear_display_settings) + .skippable(false) + .add(new SWEditNumberWithUnits(Constants.LOWMARK * Constants.MGDL_TO_MMOLL, 3d, 8d) + .preferenceId(R.string.key_low_mark) + .updateDelay(5) + .label(R.string.low_mark) + .comment(R.string.low_mark_comment)) + .add(new SWBreak()) + .add(new SWEditNumberWithUnits(Constants.HIGHMARK * Constants.MGDL_TO_MMOLL, 5d, 20d) + .preferenceId(R.string.key_high_mark) + .updateDelay(5) + .label(R.string.high_mark) + .comment(R.string.high_mark_comment)); + private SWScreen screenPermissionBattery = new SWScreen(R.string.permission) .skippable(false) .add(new SWInfotext() @@ -270,28 +292,17 @@ public class SWDefinition { .skippable(false) .add(new SWFragment(this) .add(new LocalProfileFragment())) - .validator(() -> LocalProfilePlugin.getPlugin().getProfile() != null && LocalProfilePlugin.getPlugin().getProfile().getDefaultProfile() != null && LocalProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) - .visibility(() -> LocalProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)); + .validator(() -> LocalProfilePlugin.INSTANCE.getProfile() != null && LocalProfilePlugin.INSTANCE.getProfile().getDefaultProfile() != null && LocalProfilePlugin.INSTANCE.getProfile().getDefaultProfile().isValid("StartupWizard")) + .visibility(() -> LocalProfilePlugin.INSTANCE.isEnabled(PluginType.PROFILE)); - private SWScreen screenSimpleProfile = new SWScreen(R.string.simpleprofile) - .skippable(false) - .add(new SWFragment(this) - .add(new SimpleProfileFragment())) - .validator(() -> SimpleProfilePlugin.getPlugin().getProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) - .visibility(() -> SimpleProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)); - - private SWScreen screenProfileSwitch = new SWScreen(R.string.profileswitch) + private SWScreen screenProfileSwitch = new SWScreen(R.string.careportal_profileswitch) .skippable(false) .add(new SWInfotext() .label(R.string.profileswitch_ismissing)) .add(new SWButton() - .text(R.string.profileswitch) + .text(R.string.doprofileswitch) .action(() -> { - NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); - final OptionsToShow profileSwitch = CareportalFragment.PROFILESWITCHDIRECT; - profileSwitch.executeProfileSwitch = true; - newDialog.setOptions(profileSwitch, R.string.careportal_profileswitch); - newDialog.show(getActivity().getSupportFragmentManager(), "NewNSTreatmentDialog"); + new ProfileSwitchDialog().show(getActivity().getSupportFragmentManager(), "SetupWizard"); })) .validator(() -> ProfileFunctions.getInstance().getProfile() != null) .visibility(() -> ProfileFunctions.getInstance().getProfile() == null); @@ -417,10 +428,12 @@ public class SWDefinition { add(screenSetupWizard) .add(screenLanguage) .add(screenEula) - .add(screenPermissionBattery) + .add(isRunningTest() ? null : screenPermissionBattery) // cannot mock ask battery optimalization .add(screenPermissionBt) .add(screenPermissionStore) .add(screenImport) + .add(screenUnits) + .add(displaySettings) .add(screenNsClient) .add(screenAge) .add(screenInsulin) @@ -428,7 +441,6 @@ public class SWDefinition { .add(screenProfile) .add(screenNsProfile) .add(screenLocalProfile) - .add(screenSimpleProfile) .add(screenProfileSwitch) .add(screenPump) .add(screenAps) @@ -444,10 +456,12 @@ public class SWDefinition { add(screenSetupWizard) .add(screenLanguage) .add(screenEula) - .add(screenPermissionBattery) + .add(isRunningTest() ? null : screenPermissionBattery) // cannot mock ask battery optimalization .add(screenPermissionBt) .add(screenPermissionStore) .add(screenImport) + .add(screenUnits) + .add(displaySettings) .add(screenNsClient) .add(screenAge) .add(screenInsulin) @@ -455,7 +469,6 @@ public class SWDefinition { .add(screenProfile) .add(screenNsProfile) .add(screenLocalProfile) - .add(screenSimpleProfile) .add(screenProfileSwitch) .add(screenPump) .add(screenSensitivity) @@ -467,9 +480,11 @@ public class SWDefinition { add(screenSetupWizard) .add(screenLanguage) .add(screenEula) - .add(screenPermissionBattery) + .add(isRunningTest() ? null : screenPermissionBattery) // cannot mock ask battery optimalization .add(screenPermissionStore) .add(screenImport) + .add(screenUnits) + .add(displaySettings) .add(screenNsClient) .add(screenBgSource) .add(screenAge) @@ -477,5 +492,4 @@ public class SWDefinition { .add(screenSensitivity) ; } - } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWNumberValidator.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWNumberValidator.java new file mode 100644 index 0000000000..a71da1dea7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWNumberValidator.java @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.setupwizard; + +public interface SWNumberValidator { + boolean isValid(double value); +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java index e6a4831693..b74567d027 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java @@ -8,13 +8,10 @@ import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.List; import info.nightscout.androidaps.MainActivity; @@ -37,8 +34,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; public class SetupWizardActivity extends NoSplashAppCompatActivity { - //logging - private static Logger log = LoggerFactory.getLogger(SetupWizardActivity.class); private CompositeDisposable disposable = new CompositeDisposable(); ScrollView scrollView; @@ -54,7 +49,7 @@ public class SetupWizardActivity extends NoSplashAppCompatActivity { LocaleHelper.INSTANCE.update(getApplicationContext()); setContentView(R.layout.activity_setupwizard); - scrollView = (ScrollView) findViewById(R.id.sw_scrollview); + scrollView = findViewById(R.id.sw_scrollview); Intent intent = getIntent(); currentWizardPage = intent.getIntExtra(SetupWizardActivity.INTENT_MESSAGE, 0); @@ -62,7 +57,7 @@ public class SetupWizardActivity extends NoSplashAppCompatActivity { SWScreen currentScreen = screens.get(currentWizardPage); //Set screen name - TextView screenName = (TextView) findViewById(R.id.sw_content); + TextView screenName = findViewById(R.id.sw_content); screenName.setText(currentScreen.getHeader()); swDefinition.setActivity(this); @@ -72,18 +67,6 @@ public class SetupWizardActivity extends NoSplashAppCompatActivity { } } - @Override - public void onBackPressed() { - if (currentWizardPage == 0) - OKDialog.showConfirmation(this, MainApp.gs(R.string.exitwizard), this::finish); - else showPreviousPage(null); - } - - public void exitPressed(View view) { - SP.putBoolean(R.string.key_setupwizard_processed, true); - OKDialog.showConfirmation(this, MainApp.gs(R.string.exitwizard), this::finish); - } - @Override public void onPause() { super.onPause(); @@ -157,6 +140,18 @@ public class SetupWizardActivity extends NoSplashAppCompatActivity { }); } + @Override + public void onBackPressed() { + if (currentWizardPage == 0) + OKDialog.showConfirmation(this, MainApp.gs(R.string.exitwizard), this::finish); + else showPreviousPage(null); + } + + public void exitPressed(View view) { + SP.putBoolean(R.string.key_setupwizard_processed, true); + OKDialog.showConfirmation(this, MainApp.gs(R.string.exitwizard), this::finish); + } + public void showNextPage(View view) { this.finish(); Intent intent = new Intent(this, SetupWizardActivity.class); @@ -187,7 +182,7 @@ public class SetupWizardActivity extends NoSplashAppCompatActivity { return page; page++; } - return currentWizardPage; + return Math.min(currentWizardPage, screens.size() - 1); } private int previousPage() { @@ -197,21 +192,18 @@ public class SetupWizardActivity extends NoSplashAppCompatActivity { return page; page--; } - return currentWizardPage; + return Math.max(currentWizardPage, 0); } @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (permissions.length != 0) { if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) { switch (requestCode) { case AndroidPermission.CASE_STORAGE: //show dialog after permission is granted - AlertDialog.Builder alert = new AlertDialog.Builder(this); - alert.setMessage(R.string.alert_dialog_storage_permission_text); - alert.setPositiveButton(R.string.ok, null); - alert.show(); + OKDialog.show(this, MainApp.gs(R.string.permission), MainApp.gs(R.string.alert_dialog_storage_permission_text)); break; case AndroidPermission.CASE_LOCATION: case AndroidPermission.CASE_SMS: diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditNumberWithUnits.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditNumberWithUnits.java new file mode 100644 index 0000000000..6816b6fa1f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditNumberWithUnits.java @@ -0,0 +1,98 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.graphics.Typeface; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.text.DecimalFormat; + +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.setupwizard.SWNumberValidator; +import info.nightscout.androidaps.utils.NumberPicker; +import info.nightscout.androidaps.utils.SP; +import info.nightscout.androidaps.utils.SafeParse; + + +public class SWEditNumberWithUnits extends SWItem { + + private SWNumberValidator validator = new SWNumberValidator() { + @Override + public boolean isValid(double value) { + return value >= min && value <= max; + } + }; + private int updateDelay = 0; + private double init, min, max; + + public SWEditNumberWithUnits(double defaultMMOL, double minMMOL, double maxMMOL) { + super(Type.UNITNUMBER); + init = defaultMMOL; + min = minMMOL; + max = maxMMOL; + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + + TextWatcher watcher = 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) { + if (validator != null && validator.isValid(SafeParse.stringToDouble(s.toString()))) + save(s.toString(), updateDelay); + } + + @Override + public void afterTextChanged(Editable s) { + } + }; + + TextView l = new TextView(context); + l.setId(View.generateViewId()); + l.setText(label); + l.setTypeface(l.getTypeface(), Typeface.BOLD); + layout.addView(l); + + double initValue = SP.getDouble(preferenceId, init); + initValue = Profile.toCurrentUnits(initValue); + + NumberPicker numberPicker = new NumberPicker(context); + if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) + numberPicker.setParams(initValue, min, max, 0.1d, new DecimalFormat("0.0"), false, null, watcher); + else + numberPicker.setParams(initValue, min * 18, max * 18, 1d, new DecimalFormat("0"), false, null, watcher); + +// LinearLayout.LayoutParams ll = (LinearLayout.LayoutParams) numberPicker.getLayoutParams(); +// ll.gravity = Gravity.CENTER; +// numberPicker.setLayoutParams(ll); + layout.addView(numberPicker); + + TextView c = new TextView(context); + c.setId(View.generateViewId()); + c.setText(comment); + c.setTypeface(c.getTypeface(), Typeface.ITALIC); + layout.addView(c); + + super.generateDialog(layout); + } + + public SWEditNumberWithUnits preferenceId(int preferenceId) { + this.preferenceId = preferenceId; + return this; + } + + public SWEditNumberWithUnits updateDelay(int updateDelay) { + this.updateDelay = updateDelay; + return this; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java index 50e851ed6b..b16164c064 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java @@ -5,6 +5,7 @@ import android.graphics.Typeface; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; +import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; @@ -31,19 +32,19 @@ public class SWEditString extends SWItem { Context context = layout.getContext(); TextView l = new TextView(context); - l.setId(layout.generateViewId()); + l.setId(View.generateViewId()); l.setText(label); l.setTypeface(l.getTypeface(), Typeface.BOLD); layout.addView(l); TextView c = new TextView(context); - c.setId(layout.generateViewId()); + c.setId(View.generateViewId()); c.setText(comment); c.setTypeface(c.getTypeface(), Typeface.ITALIC); layout.addView(c); EditText editText = new EditText(context); - editText.setId(layout.generateViewId()); + editText.setId(View.generateViewId()); editText.setInputType(InputType.TYPE_CLASS_TEXT); editText.setMaxLines(1); editText.setText(SP.getString(preferenceId, "")); diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java index 6931344acf..734b204ff3 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java @@ -3,6 +3,8 @@ package info.nightscout.androidaps.setupwizard.elements; import android.view.View; import android.widget.LinearLayout; +import androidx.annotation.StringRes; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +40,8 @@ public class SWItem { RADIOBUTTON, PLUGIN, BUTTON, - FRAGMENT + FRAGMENT, + UNITNUMBER } Type type; @@ -66,12 +69,12 @@ public class SWItem { return type; } - public SWItem label(int label) { + public SWItem label(@StringRes int label) { this.label = label; return this; } - public SWItem comment(int comment) { + public SWItem comment(@StringRes int comment) { this.comment = comment; return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt b/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt new file mode 100644 index 0000000000..8cf5922392 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt @@ -0,0 +1,79 @@ +package info.nightscout.androidaps.utils + +import android.app.Activity +import android.app.Application +import android.os.Bundle +import android.text.Spanned +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.L +import org.slf4j.LoggerFactory + +object ActivityMonitor : Application.ActivityLifecycleCallbacks { + private val log = LoggerFactory.getLogger(L.CORE) + override fun onActivityPaused(activity: Activity?) { + val name = activity?.javaClass?.simpleName ?: return + val resumed = SP.getLong("Monitor_" + name + "_" + "resumed", 0) + if (resumed == 0L) { + log.debug("onActivityPaused: $name resumed == 0") + return + } + val elapsed = DateUtil.now() - resumed + val total = SP.getLong("Monitor_" + name + "_total", 0) + if (total == 0L) { + SP.putLong("Monitor_" + name + "_start", DateUtil.now()) + } + SP.putLong("Monitor_" + name + "_total", total + elapsed) + log.debug("onActivityPaused: $name elapsed=$elapsed total=${total + elapsed}") + } + + override fun onActivityResumed(activity: Activity?) { + val name = activity?.javaClass?.simpleName ?: return + log.debug("onActivityResumed: $name") + SP.putLong("Monitor_" + name + "_" + "resumed", DateUtil.now()) + } + + override fun onActivityStarted(activity: Activity?) { + } + + override fun onActivityDestroyed(activity: Activity?) { + } + + override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { + } + + override fun onActivityStopped(activity: Activity?) { + } + + override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { + } + + fun toText(): String { + val keys: Map = SP.getAll() + var result = "" + for ((key, value) in keys) + if (key.startsWith("Monitor") && key.endsWith("total")) { + val v = if (value is Long) value else SafeParse.stringToLong(value as String) + val activity = key.split("_")[1].replace("Activity", "") + val duration = DateUtil.niceTimeScalar(v as Long) + val start = SP.getLong(key.replace("total", "start"), 0) + val days = T.msecs(DateUtil.now() - start).days() + result += "$activity: $duration in $days days
" + } + return result + } + + fun stats(): Spanned { + return HtmlHelper.fromHtml("
" + MainApp.gs(R.string.activitymonitor) + ":
" + toText()) + } + + fun reset() { + val keys: Map = SP.getAll() + for ((key, _) in keys) + if (key.startsWith("Monitor") && key.endsWith("total")) { + SP.remove(key) + SP.remove(key.replace("total", "start")) + SP.remove(key.replace("total", "resumed")) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java index 9f7986cb97..41e26e8e3b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java @@ -17,11 +17,13 @@ import androidx.core.content.ContextCompat; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction; +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; public class AndroidPermission { @@ -30,6 +32,7 @@ public class AndroidPermission { public static final int CASE_LOCATION = 0x3; public static final int CASE_BATTERY = 0x4; public static final int CASE_PHONE_STATE = 0x5; + public static final int CASE_SYSTEM_WINDOW = 0x6; private static boolean permission_battery_optimization_failed = false; @@ -79,7 +82,7 @@ public class AndroidPermission { } public static synchronized void notifyForSMSPermissions(Activity activity) { - if (SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)) { + if (SmsCommunicatorPlugin.INSTANCE.isEnabled(PluginType.GENERAL)) { if (permissionNotGranted(activity, Manifest.permission.RECEIVE_SMS)) { NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_SMS, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.URGENT); notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.RECEIVE_SMS, @@ -128,4 +131,22 @@ public class AndroidPermission { } else RxBus.INSTANCE.send(new EventDismissNotification(Notification.PERMISSION_LOCATION)); } + + public static synchronized void notifyForSystemWindowPermissions(Activity activity) { + // Check if Android Q or higher + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + if (!Settings.canDrawOverlays(activity)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_SYSTEM_WINDOW, MainApp.gs(R.string.needsystemwindowpermission), Notification.URGENT); + notification.action(R.string.request, () -> { + // Show alert dialog to the user saying a separate permission is needed + // Launch the settings activity if the user prefers + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:" + activity.getPackageName())); + activity.startActivity(intent); + }); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } else + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PERMISSION_SYSTEM_WINDOW)); + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.kt index ef4cae3f83..ea4da238cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.kt @@ -2,9 +2,9 @@ package info.nightscout.androidaps.utils import android.content.Context import android.content.Intent -import androidx.appcompat.app.AlertDialog import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.db.CareportalEvent @@ -20,7 +20,6 @@ import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions -import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin @@ -117,18 +116,18 @@ class BolusWizard @JvmOverloads constructor(val profile: Profile, private fun doCalc() { // Insulin from BG - sens = profile.isf - targetBGLow = profile.targetLow - targetBGHigh = profile.targetHigh + sens = Profile.fromMgdlToUnits(profile.isfMgdl, ProfileFunctions.getSystemUnits()) + targetBGLow = Profile.fromMgdlToUnits(profile.targetLowMgdl, ProfileFunctions.getSystemUnits()) + targetBGHigh = Profile.fromMgdlToUnits(profile.targetHighMgdl, ProfileFunctions.getSystemUnits()) if (useTT && tempTarget != null) { - targetBGLow = Profile.fromMgdlToUnits(tempTarget.low, profile.units) - targetBGHigh = Profile.fromMgdlToUnits(tempTarget.high, profile.units) + targetBGLow = Profile.fromMgdlToUnits(tempTarget.low, ProfileFunctions.getSystemUnits()) + targetBGHigh = Profile.fromMgdlToUnits(tempTarget.high, ProfileFunctions.getSystemUnits()) } if (useBg && bg > 0) { bgDiff = when { bg in targetBGLow..targetBGHigh -> 0.0 - bg <= targetBGLow -> bg - targetBGLow - else -> bg - targetBGHigh + bg <= targetBGLow -> bg - targetBGLow + else -> bg - targetBGHigh } insulinFromBG = bgDiff / sens } @@ -138,11 +137,10 @@ class BolusWizard @JvmOverloads constructor(val profile: Profile, glucoseStatus?.let { if (useTrend) { trend = it.short_avgdelta - insulinFromTrend = Profile.fromMgdlToUnits(trend, profile.units) * 3 / sens + insulinFromTrend = Profile.fromMgdlToUnits(trend, ProfileFunctions.getSystemUnits()) * 3 / sens } } - // Insulin from carbs ic = profile.ic insulinFromCarbs = carbs / ic @@ -185,7 +183,7 @@ class BolusWizard @JvmOverloads constructor(val profile: Profile, } val bolusStep = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.bolusStep - ?: 0.1 + ?: 0.1 calculatedTotalInsulin = Round.roundTo(calculatedTotalInsulin, bolusStep) insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(calculatedTotalInsulin)).value() @@ -236,7 +234,7 @@ class BolusWizard @JvmOverloads constructor(val profile: Profile, private fun confirmMessageAfterConstraints(pump: PumpInterface): String { - var confirmMessage = MainApp.gs(R.string.entertreatmentquestion) + var confirmMessage = "" if (insulinAfterConstraints > 0) { val pct = if (percentageCorrection != 100.0) " (" + percentageCorrection.toInt() + "%)" else "" confirmMessage += "
" + MainApp.gs(R.string.bolus) + ": " + "" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + pct + "" @@ -268,89 +266,85 @@ class BolusWizard @JvmOverloads constructor(val profile: Profile, val pump = ConfigBuilderPlugin.getPlugin().activePump ?: return if (calculatedTotalInsulin > 0.0 || carbs > 0.0) { + if (accepted) { + log.debug("guarding: already accepted") + return + } + accepted = true + val confirmMessage = confirmMessageAfterConstraints(pump) - val builder = AlertDialog.Builder(context) - builder.setTitle(MainApp.gs(R.string.confirmation)) - builder.setMessage(HtmlHelper.fromHtml(confirmMessage)) - builder.setPositiveButton(MainApp.gs(R.string.ok)) { _, _ -> - synchronized(builder) { - if (accepted) { - log.debug("guarding: already accepted") - return@setPositiveButton - } - accepted = true - if (insulinAfterConstraints > 0 || carbs > 0) { - if (useSuperBolus) { - val loopPlugin = LoopPlugin.getPlugin() - if (loopPlugin.isEnabled(PluginType.LOOP)) { - loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000) - RxBus.send(EventRefreshOverview("WizardDialog")) - } - - val pump1 = ConfigBuilderPlugin.getPlugin().activePump - - if (pump1?.pumpDescription?.tempBasalStyle == PumpDescription.ABSOLUTE) { - ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() { - override fun run() { - if (!result.success) { - val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) - i.putExtra("soundid", R.raw.boluserror) - i.putExtra("status", result.comment) - i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)) - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - MainApp.instance().startActivity(i) - } - } - }) - } else { - - ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() { - override fun run() { - if (!result.success) { - val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) - i.putExtra("soundid", R.raw.boluserror) - i.putExtra("status", result.comment) - i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)) - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - MainApp.instance().startActivity(i) - } - } - }) - } + OKDialog.showConfirmation(context, MainApp.gs(R.string.boluswizard), HtmlHelper.fromHtml(confirmMessage), Runnable { + if (insulinAfterConstraints > 0 || carbs > 0) { + if (useSuperBolus) { + log.debug("USER ENTRY: SUPERBOLUS TBR") + val loopPlugin = LoopPlugin.getPlugin() + if (loopPlugin.isEnabled(PluginType.LOOP)) { + loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000) + RxBus.send(EventRefreshOverview("WizardDialog")) } - val detailedBolusInfo = DetailedBolusInfo() - detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD - detailedBolusInfo.insulin = insulinAfterConstraints - detailedBolusInfo.carbs = carbs.toDouble() - detailedBolusInfo.context = context - detailedBolusInfo.glucose = bg - detailedBolusInfo.glucoseType = "Manual" - detailedBolusInfo.carbTime = carbTime - detailedBolusInfo.boluscalc = nsJSON() - detailedBolusInfo.source = Source.USER - detailedBolusInfo.notes = notes - if (detailedBolusInfo.insulin > 0 || ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.storesCarbInfo == true) { - ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + + val pump1 = ConfigBuilderPlugin.getPlugin().activePump + + if (pump1?.pumpDescription?.tempBasalStyle == PumpDescription.ABSOLUTE) { + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() { override fun run() { if (!result.success) { val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) i.putExtra("soundid", R.raw.boluserror) i.putExtra("status", result.comment) - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)) + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)) i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) MainApp.instance().startActivity(i) } } }) } else { - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false) + + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) } } + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD + detailedBolusInfo.insulin = insulinAfterConstraints + detailedBolusInfo.carbs = carbs.toDouble() + detailedBolusInfo.context = context + detailedBolusInfo.glucose = bg + detailedBolusInfo.glucoseType = "Manual" + detailedBolusInfo.carbTime = carbTime + detailedBolusInfo.boluscalc = nsJSON() + detailedBolusInfo.source = Source.USER + detailedBolusInfo.notes = notes + log.debug("USER ENTRY: BOLUS insulin $insulinAfterConstraints carbs: $carbs") + if (detailedBolusInfo.insulin > 0 || ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.storesCarbInfo == true) { + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + } else { + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false) + } } - } - builder.setNegativeButton(MainApp.gs(R.string.cancel), null) - builder.show() + }) } } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java index d592347d1b..aa228b96ca 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java @@ -93,15 +93,15 @@ public class DateUtil { } public static int toSeconds(String hh_colon_mm) { - Pattern p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM | PM|)"); + Pattern p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)"); Matcher m = p.matcher(hh_colon_mm); int retval = 0; if (m.find()) { retval = SafeParse.stringToInt(m.group(1)) * 60 * 60 + SafeParse.stringToInt(m.group(2)) * 60; - if ((m.group(3).equals(" a.m.") || m.group(3).equals(" AM")) && m.group(1).equals("12")) + if ((m.group(3).equals(" a.m.") || m.group(3).equals(" AM") || m.group(3).equals("AM")) && m.group(1).equals("12")) retval -= 12 * 60 * 60; - if ((m.group(3).equals(" p.m.") || m.group(3).equals(" PM")) && !(m.group(1).equals("12"))) + if ((m.group(3).equals(" p.m.") || m.group(3).equals(" PM") || m.group(3).equals("PM")) && !(m.group(1).equals("12"))) retval += 12 * 60 * 60; } return retval; @@ -117,6 +117,14 @@ public class DateUtil { return df.format(mills); } + public static String dateStringShort(long mills) { + String format = "MM/dd"; + if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) { + format = "dd/MM"; + } + return new DateTime(mills).toString(DateTimeFormat.forPattern(format)); + } + public static String timeString(Date date) { String format = "hh:mma"; if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) { @@ -133,6 +141,14 @@ public class DateUtil { return new DateTime(mills).toString(DateTimeFormat.forPattern(format)); } + public static String timeStringWithSeconds(long mills) { + String format = "hh:mm:ssa"; + if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) { + format = "HH:mm:ss"; + } + return new DateTime(mills).toString(DateTimeFormat.forPattern(format)); + } + public static String timeFullString(long mills) { return new DateTime(mills).toString(DateTimeFormat.fullTime()); } @@ -150,6 +166,11 @@ public class DateUtil { return dateString(mills) + " " + timeString(mills); } + public static String dateAndTimeAndSecondsString(long mills) { + if (mills == 0) return ""; + return dateString(mills) + " " + timeStringWithSeconds(mills); + } + public static String dateAndTimeFullString(long mills) { return dateString(mills) + " " + timeFullString(mills); } @@ -185,7 +206,7 @@ public class DateUtil { long remainingTimeMinutes = timeInMillis / (1000 * 60); long remainingTimeHours = remainingTimeMinutes / 60; remainingTimeMinutes = remainingTimeMinutes % 60; - return "(" + ((remainingTimeHours > 0) ? (remainingTimeHours + "h ") : "") + remainingTimeMinutes + "')"; + return "(" + ((remainingTimeHours > 0) ? (remainingTimeHours + MainApp.gs(R.string.shorthour) + " ") : "") + remainingTimeMinutes + "')"; } public static String sinceString(long timestamp) { @@ -209,6 +230,11 @@ public class DateUtil { return diff < T.mins(2).msecs(); } + public static boolean isOlderThan(long date, long minutes) { + long diff = now() - date; + return diff > T.mins(minutes).msecs(); + } + public static GregorianCalendar gregorianCalendar() { return new GregorianCalendar(); } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DefaultValueHelper.java b/app/src/main/java/info/nightscout/androidaps/utils/DefaultValueHelper.java deleted file mode 100644 index 40448d2680..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/DefaultValueHelper.java +++ /dev/null @@ -1,90 +0,0 @@ -package info.nightscout.androidaps.utils; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.R; - -public class DefaultValueHelper { - - /** - * returns the corresponding EatingSoon TempTarget based on the given units (MMOL / MGDL) - * - * @param units - * @return - */ - public double getDefaultEatingSoonTT(String units) { - return Constants.MMOL.equals(units) ? Constants.defaultEatingSoonTTmmol - : Constants.defaultEatingSoonTTmgdl; - } - - /** - * returns the corresponding Activity TempTarget based on the given units (MMOL / MGDL) - * - * @param units - * @return - */ - public double getDefaultActivityTT(String units) { - return Constants.MMOL.equals(units) ? Constants.defaultActivityTTmmol - : Constants.defaultActivityTTmgdl; - } - - /** - * returns the corresponding Hypo TempTarget based on the given units (MMOL / MGDL) - * - * @param units - * @return - */ - public double getDefaultHypoTT(String units) { - return Constants.MMOL.equals(units) ? Constants.defaultHypoTTmmol - : Constants.defaultHypoTTmgdl; - } - - /** - * returns the configured EatingSoon TempTarget, if this is set to 0, the Default-Value is returned. - * - * @param units - * @return - */ - public double determineEatingSoonTT(String units) { - double value = SP.getDouble(R.string.key_eatingsoon_target, this.getDefaultEatingSoonTT(units)); - return value > 0 ? value : this.getDefaultEatingSoonTT(units); - } - - public int determineEatingSoonTTDuration() { - int value = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration); - return value > 0 ? value : Constants.defaultEatingSoonTTDuration; - } - - - /** - * returns the configured Activity TempTarget, if this is set to 0, the Default-Value is returned. - * - * @param units - * @return - */ - public double determineActivityTT(String units) { - double value = SP.getDouble(R.string.key_activity_target, this.getDefaultActivityTT(units)); - return value > 0 ? value : this.getDefaultActivityTT(units); - } - - public int determineActivityTTDuration() { - int value = SP.getInt(R.string.key_activity_duration, Constants.defaultActivityTTDuration); - return value > 0 ? value : Constants.defaultActivityTTDuration; - } - - /** - * returns the configured Hypo TempTarget, if this is set to 0, the Default-Value is returned. - * - * @param units - * @return - */ - public double determineHypoTT(String units) { - double value = SP.getDouble(R.string.key_hypo_target, this.getDefaultHypoTT(units)); - return value > 0 ? value : this.getDefaultHypoTT(units); - } - - public int determineHypoTTDuration() { - int value = SP.getInt(R.string.key_hypo_duration, Constants.defaultHypoTTDuration); - return value > 0 ? value : Constants.defaultHypoTTDuration; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DefaultValueHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/DefaultValueHelper.kt new file mode 100644 index 0000000000..7b40d5d35b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/DefaultValueHelper.kt @@ -0,0 +1,95 @@ +package info.nightscout.androidaps.utils + +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions + +object DefaultValueHelper { + /** + * returns the corresponding EatingSoon TempTarget based on the given units (MMOL / MGDL) + * + * @param units + * @return + */ + fun getDefaultEatingSoonTT(units: String): Double { + return if (Constants.MMOL == units) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl + } + + /** + * returns the corresponding Activity TempTarget based on the given units (MMOL / MGDL) + * + * @param units + * @return + */ + fun getDefaultActivityTT(units: String): Double { + return if (Constants.MMOL == units) Constants.defaultActivityTTmmol else Constants.defaultActivityTTmgdl + } + + /** + * returns the corresponding Hypo TempTarget based on the given units (MMOL / MGDL) + * + * @param units + * @return + */ + fun getDefaultHypoTT(units: String): Double { + return if (Constants.MMOL == units) Constants.defaultHypoTTmmol else Constants.defaultHypoTTmgdl + } + + /** + * returns the configured EatingSoon TempTarget, if this is set to 0, the Default-Value is returned. + * + * @return + */ + @JvmStatic + fun determineEatingSoonTT(): Double { + val units = ProfileFunctions.getSystemUnits() + var value = SP.getDouble(R.string.key_eatingsoon_target, getDefaultEatingSoonTT(units)) + value = Profile.toCurrentUnits(value) + return if (value > 0) value else getDefaultEatingSoonTT(units) + } + + @JvmStatic + fun determineEatingSoonTTDuration(): Int { + val value = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration) + return if (value > 0) value else Constants.defaultEatingSoonTTDuration + } + + /** + * returns the configured Activity TempTarget, if this is set to 0, the Default-Value is returned. + * + * @return + */ + @JvmStatic + fun determineActivityTT(): Double { + val units = ProfileFunctions.getSystemUnits() + var value = SP.getDouble(R.string.key_activity_target, getDefaultActivityTT(units)) + value = Profile.toCurrentUnits(value) + return if (value > 0) value else getDefaultActivityTT(units) + } + + @JvmStatic + fun determineActivityTTDuration(): Int { + val value = SP.getInt(R.string.key_activity_duration, Constants.defaultActivityTTDuration) + return if (value > 0) value else Constants.defaultActivityTTDuration + } + + /** + * returns the configured Hypo TempTarget, if this is set to 0, the Default-Value is returned. + * + * @return + */ + @JvmStatic + fun determineHypoTT(): Double { + val units = ProfileFunctions.getSystemUnits() + var value = SP.getDouble(R.string.key_hypo_target, getDefaultHypoTT(units)) + value = Profile.toCurrentUnits(value) + return if (value > 0) value else getDefaultHypoTT(units) + } + + @JvmStatic + fun determineHypoTTDuration(): Int { + val value = SP.getInt(R.string.key_hypo_duration, Constants.defaultHypoTTDuration) + return if (value > 0) value else Constants.defaultHypoTTDuration + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/EspressoTestHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/EspressoTestHelper.kt new file mode 100644 index 0000000000..0cddcc0792 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/EspressoTestHelper.kt @@ -0,0 +1,21 @@ +package info.nightscout.androidaps.utils + +@Synchronized +fun isRunningTest(): Boolean { + return try { + Class.forName("androidx.test.espresso.Espresso") + true + } catch (e: ClassNotFoundException) { + false + } +} + +@Synchronized +fun isRunningRealPumpTest(): Boolean { + return try { + Class.forName("info.nightscout.androidaps.RealPumpTest") + true + } catch (e: ClassNotFoundException) { + false + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.java b/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.java deleted file mode 100644 index 5308c62420..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.java +++ /dev/null @@ -1,126 +0,0 @@ -package info.nightscout.androidaps.utils; - -import androidx.annotation.Nullable; - -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * JSonHelper is a Helper class which contains several methods to safely get data from the ggiven JSONObject. - * - * Created by triplem on 04.01.18. - */ - -public class JsonHelper { - - private static final Logger log = LoggerFactory.getLogger(JsonHelper.class); - - private JsonHelper() {} - - public static Object safeGetObject(JSONObject json, String fieldName, Object defaultValue) { - Object result = defaultValue; - - if (json != null && json.has(fieldName)) { - try { - result = json.get(fieldName); - } catch (JSONException ignored) { - } - } - - return result; - } - - @Nullable - public static String safeGetString(JSONObject json, String fieldName) { - String result = null; - - if (json != null && json.has(fieldName)) { - try { - result = json.getString(fieldName); - } catch (JSONException ignored) { - } - } - - return result; - } - - public static String safeGetString(JSONObject json, String fieldName, String defaultValue) { - String result = defaultValue; - - if (json != null && json.has(fieldName)) { - try { - result = json.getString(fieldName); - } catch (JSONException ignored) { - } - } - - return result; - } - - public static double safeGetDouble(JSONObject json, String fieldName) { - double result = 0d; - - if (json != null && json.has(fieldName)) { - try { - result = json.getDouble(fieldName); - } catch (JSONException ignored) { - } - } - - return result; - } - - public static double safeGetDouble(JSONObject json, String fieldName, double defaultValue) { - double result = defaultValue; - - if (json != null && json.has(fieldName)) { - try { - result = json.getDouble(fieldName); - } catch (JSONException ignored) { - } - } - - return result; - } - - public static int safeGetInt(JSONObject json, String fieldName) { - int result = 0; - - if (json != null && json.has(fieldName)) { - try { - result = json.getInt(fieldName); - } catch (JSONException ignored) { - } - } - - return result; - } - - public static long safeGetLong(JSONObject json, String fieldName) { - long result = 0; - - if (json != null && json.has(fieldName)) { - try { - result = json.getLong(fieldName); - } catch (JSONException e) { - } - } - - return result; - } - - public static boolean safeGetBoolean(JSONObject json, String fieldName) { - boolean result = false; - - if (json != null && json.has(fieldName)) { - try { - result = json.getBoolean(fieldName); - } catch (JSONException e) { - } - } - - return result; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.kt new file mode 100644 index 0000000000..484f679345 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.kt @@ -0,0 +1,126 @@ +package info.nightscout.androidaps.utils + +import org.json.JSONException +import org.json.JSONObject + +object JsonHelper { + @JvmStatic + fun safeGetObject(json: JSONObject?, fieldName: String, defaultValue: Any): Any { + var result = defaultValue + if (json != null && json.has(fieldName)) { + try { + result = json[fieldName] + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetJSONObject(json: JSONObject?, fieldName: String, defaultValue: JSONObject?): JSONObject? { + var result = defaultValue + if (json != null && json.has(fieldName)) { + try { + result = json.getJSONObject(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetString(json: JSONObject?, fieldName: String): String? { + var result: String? = null + if (json != null && json.has(fieldName)) { + try { + result = json.getString(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetString(json: JSONObject?, fieldName: String, defaultValue: String): String { + var result = defaultValue + if (json != null && json.has(fieldName)) { + try { + result = json.getString(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetStringAllowNull(json: JSONObject?, fieldName: String, defaultValue: String?): String? { + var result = defaultValue + if (json != null && json.has(fieldName)) { + try { + result = json.getString(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetDouble(json: JSONObject?, fieldName: String): Double { + var result = 0.0 + if (json != null && json.has(fieldName)) { + try { + result = json.getDouble(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetDouble(json: JSONObject?, fieldName: String, defaultValue: Double): Double { + var result = defaultValue + if (json != null && json.has(fieldName)) { + try { + result = json.getDouble(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetInt(json: JSONObject?, fieldName: String): Int { + var result = 0 + if (json != null && json.has(fieldName)) { + try { + result = json.getInt(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetLong(json: JSONObject?, fieldName: String): Long { + var result: Long = 0 + if (json != null && json.has(fieldName)) { + try { + result = json.getLong(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + + @JvmStatic + fun safeGetBoolean(json: JSONObject?, fieldName: String): Boolean { + var result = false + if (json != null && json.has(fieldName)) { + try { + result = json.getBoolean(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt index 6052c5699a..447d076d93 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt @@ -1,21 +1,19 @@ package info.nightscout.androidaps.utils import android.content.Context +import android.content.ContextWrapper +import android.os.Build +import android.os.LocaleList import info.nightscout.androidaps.R import java.util.* -object LocaleHelper { - fun update(context: Context) = - updateResources(context, currentLanguage()) +object LocaleHelper { fun currentLanguage(): String = SP.getString(R.string.key_language, Locale.getDefault().language) - fun currentLocale(): Locale = - Locale(SP.getString(R.string.key_language, Locale.getDefault().language)) - - @Suppress("DEPRECATION") - private fun updateResources(context: Context, language: String) { + private fun currentLocale(): Locale { + val language = currentLanguage() var locale = Locale(language) if (language.contains("_")) { // language with country like pt_BR defined in arrays.xml @@ -23,10 +21,35 @@ object LocaleHelper { val country = language.substring(3, 5) locale = Locale(lang, country) } + return locale + } + @Suppress("DEPRECATION") + fun update(context: Context) { + val locale = currentLocale() Locale.setDefault(locale) val resources = context.resources - resources.configuration.setLocale(locale) - resources.updateConfiguration(resources.configuration, resources.displayMetrics) + val configuration = resources.configuration + context.createConfigurationContext(configuration) + configuration.setLocale(locale) + configuration.locale = locale + resources.updateConfiguration(configuration, resources.displayMetrics) + } + + fun wrap(ctx: Context): ContextWrapper { + val res = ctx.resources + val configuration = res.configuration + val newLocale = currentLocale() + val context = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + configuration.setLocale(newLocale) + val localeList = LocaleList(newLocale) + LocaleList.setDefault(localeList) + configuration.locales = localeList + ctx.createConfigurationContext(configuration) + } else { + configuration.setLocale(newLocale) + ctx.createConfigurationContext(configuration) + } + return ContextWrapper(context) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java b/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java index e2a7489fc2..b91236ba25 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java @@ -86,6 +86,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, public NumberPicker(Context context) { super(context, null); + this.initialize(context); } public NumberPicker(Context context, AttributeSet attrs) { @@ -94,9 +95,13 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, this.initialize(context); } + protected void inflate(Context context) { + LayoutInflater.from(context).inflate(R.layout.number_picker_layout, this, true); + } + private void initialize(Context context) { // set layout view - LayoutInflater.from(context).inflate(R.layout.number_picker_layout, this, true); + inflate(context); // init ui components minusButton = findViewById(R.id.decrement); @@ -149,6 +154,11 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, }); } + @Override + public void setTag(Object tag) { + editText.setTag(tag); + } + public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) { mOnValueChangedListener = onValueChangedListener; } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.java b/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.java new file mode 100644 index 0000000000..3265280c48 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.java @@ -0,0 +1,46 @@ +package info.nightscout.androidaps.utils; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.NumberFormat; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; + +/** + * Created by mike on 28.06.2016. + */ +public class NumberPickerVertical extends NumberPicker { + private static Logger log = LoggerFactory.getLogger(NumberPickerVertical.class); + + public NumberPickerVertical(Context context) { + super(context); + } + + public NumberPickerVertical(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void inflate(Context context) { + LayoutInflater.from(context).inflate(R.layout.number_picker_layout_vertical, this, true); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.java b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.java deleted file mode 100644 index a45d329d63..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.java +++ /dev/null @@ -1,86 +0,0 @@ -package info.nightscout.androidaps.utils; - -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Handler; -import android.os.SystemClock; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.view.ContextThemeWrapper; -import android.text.Spanned; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; - -/** - * Created by mike on 31.03.2017. - */ - -public class OKDialog { - private static Logger log = LoggerFactory.getLogger(OKDialog.class); - - public static void show(final Context context, String title, String message, final Runnable runnable) { - try { - AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(context, R.style.AppTheme)); - builder.setTitle(title); - builder.setMessage(message); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - if (runnable != null) { - SystemClock.sleep(100); - runOnUiThread(runnable); - } - } - }); - - builder.create().show(); - } catch (Exception e) { - log.debug("show_dialog exception: ", e); - } - } - - public static boolean runOnUiThread(Runnable theRunnable) { - final Handler mainHandler = new Handler(MainApp.instance().getApplicationContext().getMainLooper()); - return mainHandler.post(theRunnable); - } - - public static void show(final Activity activity, String title, Spanned message, final Runnable runnable) { - try { - AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(activity, R.style.AppTheme)); - builder.setTitle(title); - builder.setMessage(message); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - if (runnable != null) { - SystemClock.sleep(100); - activity.runOnUiThread(runnable); - } - } - }); - - builder.create().show(); - } catch (Exception e) { - log.debug("show_dialog exception: " + e); - } - } - - public static void showConfirmation(final Activity activity, String message, final Runnable runnable) { - AlertDialog alertDialog = new AlertDialog.Builder(new ContextThemeWrapper(activity, R.style.AppTheme)) - .setMessage(message) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { - dialog.dismiss(); - if (runnable != null) { - SystemClock.sleep(100); - activity.runOnUiThread(runnable); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt new file mode 100644 index 0000000000..1d22c380ca --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt @@ -0,0 +1,203 @@ +package info.nightscout.androidaps.utils + +import android.app.Activity +import android.content.Context +import android.content.DialogInterface +import android.os.Handler +import android.os.SystemClock +import android.text.Spanned +import android.view.LayoutInflater +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.view.ContextThemeWrapper +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R + +object OKDialog { + @JvmStatic + @JvmOverloads + fun show(context: Context, title: String, message: String, runnable: Runnable? = null) { + var notEmptytitle = title + if (notEmptytitle.isEmpty()) notEmptytitle = MainApp.gs(R.string.message) + val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null) + (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = notEmptytitle + (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) + AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme)) + .setCustomTitle(titleLayout) + .setMessage(message) + .setPositiveButton(MainApp.gs(R.string.ok)) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + runOnUiThread(runnable) + } + .show() + .setCanceledOnTouchOutside(false) + } + + fun runOnUiThread(theRunnable: Runnable?) { + val mainHandler = Handler(MainApp.instance().applicationContext.mainLooper) + theRunnable?.let { mainHandler.post(it) } + } + + @JvmStatic + @JvmOverloads + fun show(activity: Activity, title: String, message: Spanned, runnable: Runnable? = null) { + var notEmptytitle = title + if (notEmptytitle.isEmpty()) notEmptytitle = MainApp.gs(R.string.message) + val titleLayout = activity.layoutInflater.inflate(R.layout.dialog_alert_custom, null) + (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = notEmptytitle + (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) + AlertDialog.Builder(ContextThemeWrapper(activity, R.style.AppTheme)) + .setCustomTitle(titleLayout) + .setMessage(message) + .setPositiveButton(MainApp.gs(R.string.ok)) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + runnable?.let { activity.runOnUiThread(it) } + } + .show() + .setCanceledOnTouchOutside(false) + } + + @JvmStatic + @JvmOverloads + fun showConfirmation(activity: Activity, message: String, ok: Runnable?) { + showConfirmation(activity, MainApp.gs(R.string.confirmation), message, ok, null) + } + + @JvmStatic + @JvmOverloads + fun showConfirmation(activity: Activity, message: Spanned, ok: Runnable?) { + showConfirmation(activity, MainApp.gs(R.string.confirmation), message, ok, null) + } + + @JvmStatic + @JvmOverloads + fun showConfirmation(activity: Activity, title: String, message: Spanned, ok: Runnable?, cancel: Runnable? = null) { + val titleLayout = activity.layoutInflater.inflate(R.layout.dialog_alert_custom, null) + (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title + (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) + AlertDialog.Builder(ContextThemeWrapper(activity, R.style.AppTheme)) + .setMessage(message) + .setCustomTitle(titleLayout) + .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + ok?.let { activity.runOnUiThread(it) } + } + .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + cancel?.let { activity.runOnUiThread(it) } + } + .setNegativeButton(android.R.string.cancel, null) + .show() + .setCanceledOnTouchOutside(false) + } + + @JvmStatic + fun showConfirmation(activity: Activity, title: String, message: String, ok: Runnable?, cancel: Runnable? = null) { + val titleLayout = activity.layoutInflater.inflate(R.layout.dialog_alert_custom, null) + (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title + (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) + AlertDialog.Builder(ContextThemeWrapper(activity, R.style.AppTheme)) + .setMessage(message) + .setCustomTitle(titleLayout) + .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + ok?.let { activity.runOnUiThread(it) } + } + .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + cancel?.let { activity.runOnUiThread(it) } + } + .show() + .setCanceledOnTouchOutside(false) + } + + @JvmStatic + @JvmOverloads + fun showConfirmation(context: Context, message: Spanned, ok: Runnable?, cancel: Runnable? = null) { + showConfirmation(context, MainApp.gs(R.string.confirmation), message, ok, cancel) + } + + @JvmStatic + @JvmOverloads + fun showConfirmation(context: Context, title: String, message: Spanned, ok: Runnable?, cancel: Runnable? = null) { + val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null) + (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title + (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) + AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme)) + .setMessage(message) + .setCustomTitle(titleLayout) + .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + runOnUiThread(ok) + } + .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + runOnUiThread(cancel) + } + .setNegativeButton(android.R.string.cancel, null) + .show() + .setCanceledOnTouchOutside(false) + } + + @JvmStatic + @JvmOverloads + fun showConfirmation(context: Context, message: String, ok: Runnable?, cancel: Runnable? = null) { + showConfirmation(context, MainApp.gs(R.string.confirmation), message, ok, cancel) + } + + @JvmStatic + @JvmOverloads + fun showConfirmation(context: Context, title: String, message: String, ok: Runnable?, cancel: Runnable? = null) { + val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null) + (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title + (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) + AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme)) + .setMessage(message) + .setCustomTitle(titleLayout) + .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + runOnUiThread(ok) + } + .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + SystemClock.sleep(100) + runOnUiThread(cancel) + } + .show() + .setCanceledOnTouchOutside(false) + } + + @JvmStatic + @JvmOverloads + fun showConfirmation(context: Context, title: String, message: String, ok: DialogInterface.OnClickListener?, cancel: DialogInterface.OnClickListener? = null) { + val titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null) + (titleLayout.findViewById(R.id.alertdialog_title) as TextView).text = title + (titleLayout.findViewById(R.id.alertdialog_icon) as ImageView).setImageResource(R.drawable.ic_check_while_48dp) + AlertDialog.Builder(ContextThemeWrapper(context, R.style.AppTheme)) + .setMessage(message) + .setCustomTitle(titleLayout) + .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int -> + dialog.dismiss() + SystemClock.sleep(100) + ok?.onClick(dialog, which) + } + .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, which: Int -> + dialog.dismiss() + SystemClock.sleep(100) + cancel?.onClick(dialog, which) + } + .show() + .setCanceledOnTouchOutside(false) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/PasswordProtection.java b/app/src/main/java/info/nightscout/androidaps/utils/PasswordProtection.java index 230be97e22..6c4348bb03 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/PasswordProtection.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/PasswordProtection.java @@ -2,10 +2,10 @@ package info.nightscout.androidaps.utils; import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; +import android.widget.ImageView; import android.widget.TextView; import info.nightscout.androidaps.MainApp; @@ -18,10 +18,7 @@ import info.nightscout.androidaps.R; public class PasswordProtection { static public boolean isLocked(String preference) { final String password = SP.getString(preference, ""); - if (password.equals("")) { - return false; - } - return true; + return !password.equals(""); } static public void QueryPassword(final Context context, int stringID, String preference, final Runnable ok, final Runnable fail) { @@ -30,39 +27,38 @@ public class PasswordProtection { if (ok != null) ok.run(); return; } - LayoutInflater li = LayoutInflater.from(context); - View promptsView = li.inflate(R.layout.passwordprompt, null); + View promptsView = LayoutInflater.from(context).inflate(R.layout.passwordprompt, null); + + View titleLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_custom, null); + ((TextView) titleLayout.findViewById(R.id.alertdialog_title)).setText(R.string.confirmation); + ((ImageView) titleLayout.findViewById(R.id.alertdialog_icon)).setImageResource(R.drawable.ic_check_while_48dp); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); alertDialogBuilder.setView(promptsView); + alertDialogBuilder.setCustomTitle(titleLayout); - final TextView label = (TextView) promptsView.findViewById(R.id.passwordprompt_text); + final TextView label = promptsView.findViewById(R.id.passwordprompt_text); label.setText(MainApp.gs(stringID)); - final EditText userInput = (EditText) promptsView.findViewById(R.id.passwordprompt_pass); + final EditText userInput = promptsView.findViewById(R.id.passwordprompt_pass); // set dialog message alertDialogBuilder .setCancelable(false) - .setPositiveButton("OK", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog,int id) { - String enteredPassword = userInput.getText().toString(); - if (password.equals(enteredPassword)) { - if (ok != null) ok.run(); - } else { - ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.wrongpassword)); - if (fail != null) fail.run(); - } + .setPositiveButton(android.R.string.ok, + (dialog, id) -> { + String enteredPassword = userInput.getText().toString(); + if (password.equals(enteredPassword)) { + if (ok != null) ok.run(); + } else { + ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.wrongpassword)); + if (fail != null) fail.run(); } }) - .setNegativeButton("Cancel", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog,int id) { - dialog.cancel(); - } - }); + .setNegativeButton(android.R.string.cancel, + (dialog, id) -> dialog.cancel()); AlertDialog alertDialog = alertDialogBuilder.create(); alertDialog.show(); + alertDialog.setCanceledOnTouchOutside(false); } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/Round.java b/app/src/main/java/info/nightscout/androidaps/utils/Round.java index ba7f7e3f86..3e312a20a9 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/Round.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/Round.java @@ -1,14 +1,23 @@ package info.nightscout.androidaps.utils; +import java.math.BigDecimal; + /** * Created by mike on 20.06.2016. */ public class Round { public static Double roundTo(double x, Double step) { - if (x != 0d) { - return Math.round(x / step) * step; + if (x == 0d) { + return 0d; } - return 0d; + + //Double oldCalc = Math.round(x / step) * step; + Double newCalc = BigDecimal.valueOf(Math.round(x / step)).multiply(BigDecimal.valueOf(step)).doubleValue(); + + // just for the tests, forcing failures + //newCalc = oldCalc; + + return newCalc; } public static Double floorTo(Double x, Double step) { if (x != 0d) { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SP.java b/app/src/main/java/info/nightscout/androidaps/utils/SP.java index 1b46642751..191fd9421b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/SP.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/SP.java @@ -3,6 +3,8 @@ package info.nightscout.androidaps.utils; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import java.util.Map; + import info.nightscout.androidaps.MainApp; /** @@ -10,7 +12,15 @@ import info.nightscout.androidaps.MainApp; */ public class SP { - static SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); + private static SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); + + static public Map getAll() { + return sharedPreferences.getAll(); + } + + static public void clear() { + sharedPreferences.edit().clear().apply(); + } static public boolean contains(String key) { return sharedPreferences.contains(key); @@ -85,75 +95,51 @@ public class SP { } static public void putBoolean(String key, boolean value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(key, value); - editor.apply(); + sharedPreferences.edit().putBoolean(key, value).apply(); } static public void putBoolean(int resourceID, boolean value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(MainApp.gs(resourceID), value); - editor.apply(); + sharedPreferences.edit().putBoolean(MainApp.gs(resourceID), value).apply(); } static public void putDouble(String key, double value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString(key, Double.toString(value)); - editor.apply(); + sharedPreferences.edit().putString(key, Double.toString(value)).apply(); } static public void putLong(String key, long value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putLong(key, value); - editor.apply(); + sharedPreferences.edit().putLong(key, value).apply(); } static public void putLong(int resourceID, long value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putLong(MainApp.gs(resourceID), value); - editor.apply(); + sharedPreferences.edit().putLong(MainApp.gs(resourceID), value).apply(); } static public void putInt(String key, int value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putInt(key, value); - editor.apply(); + sharedPreferences.edit().putInt(key, value).apply(); } static public void putInt(int resourceID, int value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putInt(MainApp.gs(resourceID), value); - editor.apply(); + sharedPreferences.edit().putInt(MainApp.gs(resourceID), value).apply(); } static public void incInt(int resourceID) { - SharedPreferences.Editor editor = sharedPreferences.edit(); int value = SP.getInt(resourceID, 0) + 1; - editor.putInt(MainApp.gs(resourceID), value); - editor.apply(); + sharedPreferences.edit().putInt(MainApp.gs(resourceID), value).apply(); } static public void putString(int resourceID, String value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString(MainApp.gs(resourceID), value); - editor.apply(); + sharedPreferences.edit().putString(MainApp.gs(resourceID), value).apply(); } static public void putString(String key, String value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString(key, value); - editor.apply(); + sharedPreferences.edit().putString(key, value).apply(); } static public void remove(int resourceID) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.remove(MainApp.gs(resourceID)); - editor.apply(); + sharedPreferences.edit().remove(MainApp.gs(resourceID)).apply(); } static public void remove(String key) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.remove(key); - editor.apply(); + sharedPreferences.edit().remove(key).apply(); } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java b/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java index fcee3638b9..60de82a93c 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java @@ -16,12 +16,16 @@ package info.nightscout.androidaps.utils; */ import android.os.SystemClock; -import android.util.Log; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; +import info.nightscout.androidaps.logging.L; + /** * {@hide} *

@@ -35,7 +39,7 @@ import java.net.InetAddress; * */ public class SntpClient { - private static final String TAG = "SntpClient"; + private static Logger log = LoggerFactory.getLogger(L.CORE); //private static final int REFERENCE_TIME_OFFSET = 16; private static final int ORIGINATE_TIME_OFFSET = 24; @@ -76,8 +80,10 @@ public class SntpClient { } static void doNtpTime(final Callback callback) { + log.debug("Time detection started"); callback.success = requestTime("time.google.com", 5000); callback.time = getNtpTime() + SystemClock.elapsedRealtime() - getNtpTimeReference(); + log.debug("Time detection ended: " + callback.success + " " + DateUtil.dateAndTimeString(getNtpTime())); callback.run(); } @@ -138,7 +144,7 @@ public class SntpClient { mNtpTimeReference = responseTicks; mRoundTripTime = roundTripTime; } catch (Exception e) { - Log.d(TAG, "request time failed: " + e); + log.debug("request time failed: " + e); return false; } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TIR.kt b/app/src/main/java/info/nightscout/androidaps/utils/TIR.kt new file mode 100644 index 0000000000..5f2ea9bbaf --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/TIR.kt @@ -0,0 +1,26 @@ +package info.nightscout.androidaps.utils + +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import kotlin.math.roundToInt + +class TIR(val date: Long, val lowThreshold: Double, val highThreshold: Double) { + internal var below = 0 + internal var inRange = 0 + internal var above = 0 + internal var error = 0 + internal var count = 0 + + fun error() = run { error++ } + fun below() = run { below++; count++ } + fun inRange() = run { inRange++; count++ } + fun above() = run { above++; count++ } + + fun belowPct() = if (count > 0) (below.toDouble() / count * 100.0).roundToInt() else 0 + fun inRangePct() = if (count > 0) (inRange.toDouble() / count * 100.0).roundToInt() else 0 + fun abovePct() = if (count > 0) (above.toDouble() / count * 100.0).roundToInt() else 0 + + fun toText(): String = MainApp.gs(R.string.tirformat, DateUtil.dateStringShort(date), belowPct(), inRangePct(), abovePct()) + + fun toText(days: Int): String = MainApp.gs(R.string.tirformat, "%02d".format(days) + " " + MainApp.gs(R.string.days), belowPct(), inRangePct(), abovePct()) +} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TddCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/TddCalculator.kt new file mode 100644 index 0000000000..0210247eb3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/TddCalculator.kt @@ -0,0 +1,81 @@ +package info.nightscout.androidaps.utils + +import android.text.Spanned +import android.util.LongSparseArray +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.db.TDD +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import org.slf4j.LoggerFactory + +object TddCalculator : TreatmentsPlugin() { + private val log = LoggerFactory.getLogger(L.DATATREATMENTS) + + fun calculate(days: Long): LongSparseArray { + val range = T.days(days + 1).msecs() + val startTime = MidnightTime.calc(DateUtil.now()) - T.days(days).msecs() + val endTime = MidnightTime.calc(DateUtil.now()) + initializeData(range) + + val result = LongSparseArray() + for (t in treatmentsFromHistory) { + if (!t.isValid) continue + if (t.date < startTime || t.date > endTime) continue + val midnight = MidnightTime.calc(t.date) + val tdd = result[midnight] ?: TDD(midnight, 0.0, 0.0, 0.0) + tdd.bolus += t.insulin + result.put(midnight, tdd) + } + + for (t in startTime until endTime step T.mins(5).msecs()) { + val midnight = MidnightTime.calc(t) + val tdd = result[midnight] ?: TDD(midnight, 0.0, 0.0, 0.0) + val tbr = getTempBasalFromHistory(t) + val profile = ProfileFunctions.getInstance().getProfile(t) ?: continue + val absoluteRate = tbr?.tempBasalConvertedToAbsolute(t, profile) ?: profile.getBasal(t) + tdd.basal += absoluteRate / 60.0 * 5.0 + result.put(midnight, tdd) + } + for (i in 0 until result.size()) { + val tdd = result.valueAt(i) + tdd.total = tdd.bolus + tdd.basal + } + log.debug(result.toString()) + return result + } + + fun averageTDD(tdds: LongSparseArray): TDD { + val totalTdd = TDD() + for (i in 0 until tdds.size()) { + val tdd = tdds.valueAt(i) + totalTdd.basal += tdd.basal + totalTdd.bolus += tdd.bolus + totalTdd.total += tdd.total + } + totalTdd.basal /= tdds.size().toDouble() + totalTdd.bolus /= tdds.size().toDouble() + totalTdd.total /= tdds.size().toDouble() + return totalTdd + } + + fun stats(): Spanned { + val tdds = calculate(7) + val averageTdd = averageTDD(tdds) + return HtmlHelper.fromHtml( + "" + MainApp.gs(R.string.tdd) + ":
" + + toText(tdds) + + "" + MainApp.gs(R.string.average) + ":
" + + averageTdd.toText(tdds.size()) + ) + } + + fun toText(tdds: LongSparseArray): String { + var t = "" + for (i in 0 until tdds.size()) { + t += "${tdds.valueAt(i).toText()}
" + } + return t + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java index be8b89dff4..0143377bb2 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java @@ -50,6 +50,7 @@ public class TimeListEdit { private Context context; private View view; private int resLayoutId; + private String tagPrefix; private String label; private JSONArray data1; private JSONArray data2; @@ -63,10 +64,11 @@ public class TimeListEdit { private int inflatedUntil = -1; - public TimeListEdit(Context context, View view, int resLayoutId, String label, JSONArray data1, JSONArray data2, double min, double max, double step, NumberFormat formatter, Runnable save) { + public TimeListEdit(Context context, View view, int resLayoutId, String tagPrefix, String label, JSONArray data1, JSONArray data2, double min, double max, double step, NumberFormat formatter, Runnable save) { this.context = context; this.view = view; this.resLayoutId = resLayoutId; + this.tagPrefix = tagPrefix; this.label = label; this.data1 = data1; this.data2 = data2; @@ -84,11 +86,11 @@ public class TimeListEdit { textlabel = new TextView(context); textlabel.setText(label); - textlabel.setGravity(Gravity.START); + textlabel.setGravity(Gravity.CENTER); LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - llp.setMargins(10, 0, 0, 0); // llp.setMargins(left, top, right, bottom); + llp.setMargins(0, 5, 0, 5); textlabel.setLayoutParams(llp); - textlabel.setBackgroundColor(ContextCompat.getColor(MainApp.instance(), R.color.linearBlockBackground)); + //textlabel.setBackgroundColor(ContextCompat.getColor(MainApp.instance(), R.color.linearBlockBackground)); TextViewCompat.setTextAppearance(textlabel, android.R.style.TextAppearance_Medium); layout.addView(textlabel); @@ -119,7 +121,8 @@ public class TimeListEdit { private void inflateRow(final int position) { LayoutInflater inflater = LayoutInflater.from(context); - View childView = intervals[position] = inflater.inflate(R.layout.timelistedit_element, layout, false); + int resource = data2 == null ? R.layout.timelistedit_element : R.layout.timelistedit_element_vertical; + View childView = intervals[position] = inflater.inflate(resource, layout, false); spinners[position] = new SpinnerHelper(childView.findViewById(R.id.timelistedit_time)); numberPickers1[position] = childView.findViewById(R.id.timelistedit_edit1); numberPickers2[position] = childView.findViewById(R.id.timelistedit_edit2); @@ -184,7 +187,7 @@ public class TimeListEdit { int before, int count) { } }); - + numberPickers1[position].setTag(tagPrefix +"-1-" + position); numberPickers2[position].setTextWatcher(new TextWatcher() { @Override @@ -204,6 +207,7 @@ public class TimeListEdit { int before, int count) { } }); + numberPickers2[position].setTag(tagPrefix +"-2-" + position); layout.addView(childView); } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TirCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/TirCalculator.kt new file mode 100644 index 0000000000..4e41f9a331 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/TirCalculator.kt @@ -0,0 +1,85 @@ +package info.nightscout.androidaps.utils + +import android.text.Spanned +import android.util.LongSparseArray +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile + +object TirCalculator { + fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray { + if (lowMgdl < 39) throw RuntimeException("Low below 39") + if (lowMgdl > highMgdl) throw RuntimeException("Low > High") + val startTime = MidnightTime.calc(DateUtil.now()) - T.days(days).msecs() + val endTime = MidnightTime.calc(DateUtil.now()) + + val bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime(startTime, endTime, true) + val result = LongSparseArray() + for (bg in bgReadings) { + val midnight = MidnightTime.calc(bg.date) + var tir = result[midnight] + if (tir == null) { + tir = TIR(midnight, lowMgdl, highMgdl) + result.append(midnight, tir) + } + if (bg.value < 39) tir.error() + if (bg.value >= 39 && bg.value < lowMgdl) tir.below() + if (bg.value in lowMgdl..highMgdl) tir.inRange() + if (bg.value > highMgdl) tir.above() + } + return result + } + + fun averageTIR(tirs: LongSparseArray): TIR { + val totalTir = if (tirs.size() > 0) { + TIR(tirs.valueAt(0).date, tirs.valueAt(0).lowThreshold, tirs.valueAt(0).highThreshold) + } else { + TIR(7, 70.0, 180.0) + } + for (i in 0 until tirs.size()) { + val tir = tirs.valueAt(i) + totalTir.below += tir.below + totalTir.inRange += tir.inRange + totalTir.above += tir.above + totalTir.error += tir.error + totalTir.count += tir.count + } + return totalTir + } + + fun stats(): Spanned { + val lowTirMgdl = Constants.STATS_RANGE_LOW_MMOL * Constants.MMOLL_TO_MGDL + val highTirMgdl = Constants.STATS_RANGE_HIGH_MMOL * Constants.MMOLL_TO_MGDL + val lowTitMgdl = Constants.STATS_TARGET_LOW_MMOL * Constants.MMOLL_TO_MGDL + val highTitMgdl = Constants.STATS_TARGET_HIGH_MMOL * Constants.MMOLL_TO_MGDL + + val tir7 = calculate(7, lowTirMgdl, highTirMgdl) + val averageTir7 = averageTIR(tir7) + val tir30 = calculate(30, lowTirMgdl, highTirMgdl) + val averageTir30 = averageTIR(tir30) + val tit7 = calculate(7, lowTitMgdl, highTitMgdl) + val averageTit7 = averageTIR(tit7) + val tit30 = calculate(30, lowTitMgdl, highTitMgdl) + val averageTit30 = averageTIR(tit30) + return HtmlHelper.fromHtml( + "
" + MainApp.gs(R.string.tir) + ":
" + + toText(tir7) + + "
" + MainApp.gs(R.string.average) + " (" + Profile.toCurrentUnitsString(lowTirMgdl) + "-" + Profile.toCurrentUnitsString(highTirMgdl) + "):
" + + averageTir7.toText(tir7.size()) + "
" + + averageTir30.toText(tir30.size()) + + "
" + MainApp.gs(R.string.average) + " (" + Profile.toCurrentUnitsString(lowTitMgdl) + "-" + Profile.toCurrentUnitsString(highTitMgdl) + "):
" + + averageTit7.toText(tit7.size()) + "
" + + averageTit30.toText(tit30.size()) + ) + } + + fun toText(tirs: LongSparseArray): String { + var t = "" + for (i in 0 until tirs.size()) { + t += "${tirs.valueAt(i).toText()}
" + } + return t + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/UIUtils.kt b/app/src/main/java/info/nightscout/androidaps/utils/UIUtils.kt new file mode 100644 index 0000000000..99c4a4af31 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/UIUtils.kt @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.utils + +import android.view.View + +/** + * Created by adrian on 2019-12-20. + */ + +fun Boolean.toVisibility() = if (this) View.VISIBLE else View.GONE + diff --git a/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.java b/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.java index f7b8761823..4354ccd528 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.java @@ -1,11 +1,9 @@ package info.nightscout.androidaps.utils; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Bundle; -import androidx.appcompat.app.AlertDialog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,20 +23,10 @@ import info.nightscout.androidaps.services.Intents; public class XdripCalibrations { private static Logger log = LoggerFactory.getLogger(XdripCalibrations.class); - public static void confirmAndSendCalibration(final Double bg, Context parentContext) { + public static void confirmAndSendCalibration(final Double bg, final Context parentContext) { if (parentContext != null) { String confirmMessage = String.format(MainApp.gs(R.string.send_calibration), bg); - - AlertDialog.Builder builder = new AlertDialog.Builder(parentContext); - builder.setTitle(MainApp.gs(R.string.confirmation)); - builder.setMessage(confirmMessage); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - sendIntent(bg); - } - }); - builder.setNegativeButton(MainApp.gs(R.string.cancel), null); - builder.show(); + OKDialog.showConfirmation(parentContext, confirmMessage, () -> sendIntent(bg)); } } @@ -46,7 +34,7 @@ public class XdripCalibrations { Context context = MainApp.instance().getApplicationContext(); Bundle bundle = new Bundle(); bundle.putDouble("glucose_number", bg); - bundle.putString("units", ProfileFunctions.getInstance().getProfileUnits().equals(Constants.MGDL) ? "mgdl" : "mmol"); + bundle.putString("units", ProfileFunctions.getSystemUnits().equals(Constants.MGDL) ? "mgdl" : "mmol"); bundle.putLong("timestamp", System.currentTimeMillis()); Intent intent = new Intent(Intents.ACTION_REMOTE_CALIBRATION); intent.putExtras(bundle); diff --git a/app/src/main/res/drawable/clone.png b/app/src/main/res/drawable/clone.png new file mode 100644 index 0000000000..82fd90cca8 Binary files /dev/null and b/app/src/main/res/drawable/clone.png differ diff --git a/app/src/main/res/drawable/ic_action_orange_48dp.xml b/app/src/main/res/drawable/ic_action_orange_48dp.xml new file mode 100644 index 0000000000..07e8c306a8 --- /dev/null +++ b/app/src/main/res/drawable/ic_action_orange_48dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_bluetooth_white_48dp.xml b/app/src/main/res/drawable/ic_bluetooth_white_48dp.xml new file mode 100644 index 0000000000..f5f2fa4797 --- /dev/null +++ b/app/src/main/res/drawable/ic_bluetooth_white_48dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_check_while_48dp.xml b/app/src/main/res/drawable/ic_check_while_48dp.xml new file mode 100644 index 0000000000..2c136685ec --- /dev/null +++ b/app/src/main/res/drawable/ic_check_while_48dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_error_red_48dp.xml b/app/src/main/res/drawable/ic_error_red_48dp.xml new file mode 100644 index 0000000000..b3666f2662 --- /dev/null +++ b/app/src/main/res/drawable/ic_error_red_48dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_reorder_gray_24dp.xml b/app/src/main/res/drawable/ic_reorder_gray_24dp.xml new file mode 100644 index 0000000000..8c12d67379 --- /dev/null +++ b/app/src/main/res/drawable/ic_reorder_gray_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_trending_flat_white_48dp.xml b/app/src/main/res/drawable/ic_trending_flat_white_48dp.xml new file mode 100644 index 0000000000..a9bd780027 --- /dev/null +++ b/app/src/main/res/drawable/ic_trending_flat_white_48dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_trigger_green_48dp.xml b/app/src/main/res/drawable/ic_trigger_green_48dp.xml new file mode 100644 index 0000000000..1bfc8d3e0d --- /dev/null +++ b/app/src/main/res/drawable/ic_trigger_green_48dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/actions_fragment.xml b/app/src/main/res/layout/actions_fragment.xml index 3e809b3b02..77a5ab00dd 100644 --- a/app/src/main/res/layout/actions_fragment.xml +++ b/app/src/main/res/layout/actions_fragment.xml @@ -1,138 +1,275 @@ - - + android:layout_height="wrap_content" + android:orientation="vertical"> - + android:layout_marginTop="15dp" + android:paddingStart="15dp" + android:paddingEnd="15dp" + android:text="@string/actions" /> + + + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/careportal_profileswitch" + + app:layout_column="0" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="0" /> + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/careportal_temporarytarget" + + app:layout_column="1" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="0" /> + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/overview_tempbasal_button" + + app:layout_column="0" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="1" /> + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/canceltemp" + android:visibility="gone" + + app:layout_column="0" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="1" /> + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/overview_extendedbolus_button" + + app:layout_column="1" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="1" /> + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/overview_extendedbolus_cancel_button" + android:visibility="gone" + + app:layout_column="1" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="1" /> + + + + + + + + + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/primefill" + + app:layout_column="1" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="2" /> + + + + + + + + + + + + + + + + + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/nav_historybrowser" + app:layout_column="0" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="4" /> + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/danar_stats_tdd" - + app:layout_column="1" + app:layout_columnWeight="1" + app:layout_gravity="fill" + app:layout_row="4" /> - + - + + + diff --git a/app/src/main/res/layout/activity_agreement.xml b/app/src/main/res/layout/activity_agreement.xml deleted file mode 100644 index 5c98f937ed..0000000000 --- a/app/src/main/res/layout/activity_agreement.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - -