diff --git a/app/build.gradle b/app/build.gradle
index 0c5fe18a24..6d781a8962 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -266,7 +266,10 @@ dependencies {
exclude group: "org.json", module: "json"
}
implementation "com.google.code.gson:gson:2.8.6"
- implementation "com.google.guava:guava:24.1-jre"
+ 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.10.3"
@@ -284,6 +287,7 @@ dependencies {
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"
@@ -293,9 +297,6 @@ 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"
@@ -307,6 +308,10 @@ dependencies {
implementation "com.squareup.retrofit2:adapter-rxjava2:2.6.2"
implementation "com.squareup.retrofit2:converter-gson:2.6.2"
+ 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'
}
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..b50bb746fb
--- /dev/null
+++ b/app/src/androidTest/java/info/nightscout/androidaps/EspressoHelper.kt
@@ -0,0 +1,27 @@
+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
+
+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)
+}
+
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..dd1c0b12bc
--- /dev/null
+++ b/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt
@@ -0,0 +1,222 @@
+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.*
+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.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.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() {
+ 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())
+ // Loction 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())
+ }
+ // 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())
+ onView(Matchers.allOf(withText("OK"), isDisplayed())).perform(click())
+ 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())
+ 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/java/info/nightscout/androidaps/Constants.java b/app/src/main/java/info/nightscout/androidaps/Constants.java
index 21a89d8920..92833ac283 100644
--- a/app/src/main/java/info/nightscout/androidaps/Constants.java
+++ b/app/src/main/java/info/nightscout/androidaps/Constants.java
@@ -78,4 +78,8 @@ 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;
+
}
diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java
index 64d260c99d..4802655d3a 100644
--- a/app/src/main/java/info/nightscout/androidaps/MainApp.java
+++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java
@@ -51,6 +51,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.receivers.DBAccessRec
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin;
import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin;
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin;
+import info.nightscout.androidaps.plugins.source.RandomBgPlugin;
import info.nightscout.androidaps.utils.ActivityMonitor;
import info.nightscout.androidaps.plugins.general.wear.WearPlugin;
import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin;
@@ -212,6 +213,7 @@ public class MainApp extends Application {
pluginsList.add(SourcePoctechPlugin.getPlugin());
pluginsList.add(SourceTomatoPlugin.getPlugin());
pluginsList.add(SourceEversensePlugin.getPlugin());
+ pluginsList.add(RandomBgPlugin.INSTANCE);
if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.INSTANCE);
pluginsList.add(FoodPlugin.getPlugin());
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/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt
index 34aadf84c6..9864f875e4 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
@@ -28,6 +28,7 @@ import info.nightscout.androidaps.plugins.constraints.objectives.events.EventNtp
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
@@ -217,6 +218,7 @@ class ObjectivesFragment : Fragment() {
scrollToCurrentObjective()
startUpdateTimer()
RxBus.send(EventObjectivesUpdateGui())
+ RxBus.send(EventSWUpdate(false))
} else {
// move out of UI thread
Thread {
@@ -234,6 +236,7 @@ class ObjectivesFragment : Fragment() {
RxBus.send(EventNtpStatus(MainApp.gs(R.string.success), 100))
SystemClock.sleep(1000)
RxBus.send(EventObjectivesUpdateGui())
+ RxBus.send(EventSWUpdate(false))
SystemClock.sleep(100)
scrollToCurrentObjective()
} else {
@@ -254,6 +257,7 @@ class ObjectivesFragment : Fragment() {
scrollToCurrentObjective()
startUpdateTimer()
RxBus.send(EventObjectivesUpdateGui())
+ RxBus.send(EventSWUpdate(false))
} else
// move out of UI thread
Thread {
@@ -270,6 +274,7 @@ class ObjectivesFragment : Fragment() {
RxBus.send(EventNtpStatus(MainApp.gs(R.string.success), 100))
SystemClock.sleep(1000)
RxBus.send(EventObjectivesUpdateGui())
+ RxBus.send(EventSWUpdate(false))
SystemClock.sleep(100)
scrollToCurrentObjective()
} else {
@@ -284,12 +289,14 @@ class ObjectivesFragment : Fragment() {
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 && objective.specialActionEnabled()) {
// generate random request code if none exists
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 96d6396f87..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,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview
+import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.events.EventRefreshOverview
@@ -65,14 +66,14 @@ object OverviewPlugin : PluginBase(PluginDescription()
fun determineHighLine(): Double {
var highLineSetting = SP.getDouble(R.string.key_high_mark, bgTargetHigh)
- if (highLineSetting < 1) highLineSetting = 180.0
+ if (highLineSetting < 1) highLineSetting = Constants.HIGHMARK
highLineSetting = Profile.toCurrentUnits(highLineSetting)
return highLineSetting
}
fun determineLowLine(): Double {
var lowLineSetting = SP.getDouble(R.string.key_low_mark, bgTargetLow)
- if (lowLineSetting < 1) lowLineSetting = 76.0
+ if (lowLineSetting < 1) lowLineSetting = Constants.LOWMARK
lowLineSetting = Profile.toCurrentUnits(lowLineSetting)
return lowLineSetting
}
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
index 3b6dbe7315..777afc7331 100644
--- 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
@@ -94,14 +94,15 @@ class LocalProfileFragment : Fragment() {
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)
- TimeListEdit(context, view, R.id.localprofile_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, MainApp.gs(R.string.nsprofileview_basal_label) + ": " + sumLabel(), LocalProfilePlugin.currentProfile().basal, null, pumpDescription.basalMinimumRate, 10.0, 0.01, DecimalFormat("0.00"), save)
+ 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, 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, 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)
+ 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, 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, 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)
+ 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
@@ -181,10 +182,11 @@ class LocalProfileFragment : Fragment() {
@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)
- TimeListEdit(context, view, R.id.localprofile_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, 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, 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, MainApp.gs(R.string.nsprofileview_target_label) + ":", LocalProfilePlugin.currentProfile().targetLow, LocalProfilePlugin.currentProfile().targetHigh, 3.0, 200.0, 0.1, DecimalFormat("0.0"), save)
+ 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()
}
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..ea6731ca60
--- /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 = 5L // 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 VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP) && (MainApp.engineeringMode || isRunningTest())
+ }
+
+ 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) * 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/setupwizard/SWDefinition.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java
index 37cfe695b4..45ab985fce 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;
@@ -49,6 +50,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<>();
@@ -66,7 +69,7 @@ public class SWDefinition {
}
private SWDefinition add(SWScreen newScreen) {
- screens.add(newScreen);
+ if (newScreen != null) screens.add(newScreen);
return this;
}
@@ -119,18 +122,17 @@ public class SWDefinition {
private SWScreen displaySettings = new SWScreen(R.string.wear_display_settings)
.skippable(false)
- .add(new SWEditNumberWithUnits(4d, 3d, 8d)
+ .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(10d, 5d, 20d)
+ .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))
- .validator(() -> SP.contains(R.string.key_low_mark) && SP.contains(R.string.key_high_mark));
+ .comment(R.string.high_mark_comment));
private SWScreen screenPermissionBattery = new SWScreen(R.string.permission)
.skippable(false)
@@ -300,7 +302,7 @@ public class SWDefinition {
.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;
@@ -432,7 +434,7 @@ 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)
@@ -460,7 +462,7 @@ 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)
@@ -484,7 +486,7 @@ 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)
@@ -496,5 +498,4 @@ public class SWDefinition {
.add(screenSensitivity)
;
}
-
}
\ No newline at end of file
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 603292d3cc..8d0d37e003 100644
--- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java
+++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java
@@ -187,7 +187,7 @@ public class SetupWizardActivity extends NoSplashAppCompatActivity {
return page;
page++;
}
- return currentWizardPage;
+ return Math.min(currentWizardPage, screens.size() -1);
}
private int previousPage() {
@@ -197,7 +197,7 @@ public class SetupWizardActivity extends NoSplashAppCompatActivity {
return page;
page--;
}
- return currentWizardPage;
+ return Math.max(currentWizardPage, 0);
}
@Override
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..7288e6679f
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/utils/EspressoTestHelper.kt
@@ -0,0 +1,11 @@
+package info.nightscout.androidaps.utils
+
+@Synchronized
+fun isRunningTest(): Boolean {
+ return try {
+ Class.forName("androidx.test.espresso.Espresso")
+ true
+ } catch (e: ClassNotFoundException) {
+ false
+ }
+}
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 54e676a52d..b91236ba25 100644
--- a/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java
+++ b/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java
@@ -154,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/TimeListEdit.java b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java
index d9ed263c98..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;
@@ -185,7 +187,7 @@ public class TimeListEdit {
int before, int count) {
}
});
-
+ numberPickers1[position].setTag(tagPrefix +"-1-" + position);
numberPickers2[position].setTextWatcher(new TextWatcher() {
@Override
@@ -205,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/res/layout/activity_setupwizard.xml b/app/src/main/res/layout/activity_setupwizard.xml
index 2c2930b827..1a9a7aad94 100644
--- a/app/src/main/res/layout/activity_setupwizard.xml
+++ b/app/src/main/res/layout/activity_setupwizard.xml
@@ -13,6 +13,7 @@
android:orientation="horizontal">
Next unfinished
Request code: %1$s
(check all correct answers)
- https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#what-to-do-when-taking-a-shower-or-bath
- https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#the-homescreen
- https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#config-builder
- https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#the-homescreen
+ https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#what-to-do-when-taking-a-shower-or-bath
+ https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#the-homescreen
+ https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#config-builder
+ https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/Screenshots.html#the-homescreen
Not connected to the internet
Failed retrieve time
Objective requirements not met
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0ae7ebf708..29a40248e8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -661,6 +661,7 @@
Basal type
Invalid profile !!!
ProfileSwitch
+ Do Profile Switch
Pump battery age
Pump Battery Change
Alarm options
@@ -1679,5 +1680,8 @@
Activity monitor
Do you want to reset activity stats?
Statistics
+ Random BG
+ Generate random BG data (Demo mode only)
+ BG