diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 9dbc9c5c83..4e46faf676 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -29,6 +29,7 @@ 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.DstHelper.DstHelperPlugin; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin; import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin; @@ -195,6 +196,8 @@ public class MainApp extends Application { pluginsList.add(ConfigBuilderPlugin.getPlugin()); + pluginsList.add(DstHelperPlugin.getPlugin()); + ConfigBuilderPlugin.getPlugin().initialize(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/DstHelper/DstHelperPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/DstHelper/DstHelperPlugin.java new file mode 100644 index 0000000000..791dcb11b6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/DstHelper/DstHelperPlugin.java @@ -0,0 +1,128 @@ +package info.nightscout.androidaps.plugins.DstHelper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +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.logging.L; +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; +import info.nightscout.androidaps.utils.T; + +/** + * Created by Rumen on 31.10.2018. + */ +public class DstHelperPlugin extends PluginBase implements ConstraintsInterface { + private static Logger log = LoggerFactory.getLogger(L.CONSTRAINTS); + private int minutesToChange = 0; + + static DstHelperPlugin plugin = null; + public static DstHelperPlugin getPlugin() { + if (plugin == null) + plugin = new DstHelperPlugin(); + return plugin; + } + + public DstHelperPlugin() { + super(new PluginDescription() + .mainType(PluginType.CONSTRAINTS) + .neverVisible(true) + .alwaysEnabled(true) + .showInList(false) + .pluginName(R.string.dst_plugin_name) + ); + } + + public int dstTest(Calendar c) throws Exception { +// c = Calendar.getInstance(TimeZone.getDefault()); +// c = Calendar.getInstance(TimeZone.getTimeZone("Australia/Lord_Howe")); +// c.setTimeInMillis(DateUtil.fromISODateString("2018-10-07T01:00:00Z").getTime()); + long zoneOffset = c.get(Calendar.ZONE_OFFSET); + long d1 = c.getTimeInMillis()-zoneOffset; + c.setTimeInMillis(d1); + int offset1 = c.get(Calendar.DST_OFFSET); + + c.add(Calendar.DATE, 1); + long d2 = c.getTimeInMillis(); + + int diffInHours = (int) ((d1 - d2) / -T.hours(1).msecs()); + long offsetDetectedTime = 0; + // comparing millis because change can be < 1 hour +// log.debug("Starting from: "+startTimeString + " to "+endTimeString); +// log.debug("start "+offset1+" end "+c.get(Calendar.DST_OFFSET)); + if (offset1 != c.get(Calendar.DST_OFFSET)) { + //we have a time change in next 24 hours, but when exactly +// log.debug("Daylight saving time detected between " + startTimeString + " and " + endTimeString); +// log.debug("Diff in hours is: "+diffInHours); + c.setTimeInMillis(d1-zoneOffset); + offset1 = c.get(Calendar.DST_OFFSET); + for(int i = 0; i <= diffInHours*4; i++){ + + if(offset1 != c.get(Calendar.DST_OFFSET)){ + log.debug("Detected offset in "+((i/4)-zoneOffset/T.hours(1).msecs())+" hours value is "+(offset1 - c.get(Calendar.DST_OFFSET))/T.mins(1).msecs()+" minutes"); + offsetDetectedTime = c.getTimeInMillis() - d1; + break; + } + c.add(Calendar.MINUTE,15); + + } + } + int minutesLeft = (int) ((offsetDetectedTime/T.mins(1).msecs())); + /*log.debug("zoneoffset(minutes):"+zoneOffset/T.mins(1).msecs()); + log.debug("Start offset: "+offset1/T.mins(1).msecs()); + log.debug("End offset :" + c.get(Calendar.DST_OFFSET)/T.mins(1).msecs()); + log.debug("Now is:"+startTimeString); + log.debug("Detected in(min): "+(offsetDetectedTime/T.mins(1).msecs())); + log.debug("Returning value of: " + minutesLeft); */ + minutesToChange = minutesLeft; + return minutesLeft; + + } + + //Return false if time to DST change is less than 91 and positive + @Override + public Constraint isLoopInvocationAllowed(Constraint value){ + try { + this.dstTest(Calendar.getInstance()); + } catch (Exception e) { + e.printStackTrace(); + } + if ( this.minutesToChange <= 90 && minutesToChange > 0 && value.value()) { + try { + LoopPlugin loopPlugin = LoopPlugin.getPlugin(); + if( loopPlugin.suspendedTo() == 0L) { +// loopPlugin.suspendTo(System.currentTimeMillis() + minutesToChange * T.mins(1).msecs()); + warnUser(Notification.DST_LOOP_DISABLED, MainApp.gs(R.string.dst_loop_disabled_warning)); + } else + log.debug("Loop already suspended"); + + } catch (Exception e) { + e.printStackTrace(); + } + value.set(false, "DST in 90 minutes or less", this); + } else if (minutesToChange <= 24 * T.hours(1).mins() && minutesToChange > 0) { + warnUser(Notification.DST_IN_24H, MainApp.gs(R.string.dst_in_24h_warning)); + } + return value; + } + + // display warning + void warnUser(int id, String warningText){ + Notification notification = new Notification(id, warningText, Notification.LOW); + MainApp.bus().post(new EventNewNotification(notification)); + } + +} \ No newline at end of file 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 bd84928845..5477963240 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 @@ -71,6 +71,8 @@ public class Notification { public static final int PERMISSION_PHONESTATE = 46; public static final int INSIGHT_DATE_TIME_UPDATED = 47; public static final int INSIGHT_TIMEOUT_DURING_HANDSHAKE = 48; + public static final int DST_LOOP_DISABLED = 49; + public static final int DST_IN_24H = 50; public int id; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dca993c5c9..e610b4af2c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1309,6 +1309,9 @@ Tomato smbmaxminutes + Dayligh Saving time + Dayligh Saving time change in 24h or less + Dayligh Saving time change in less than 3 hours - Closed loop diabled %1$d day diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/DstHelper/DstHelperPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/DstHelper/DstHelperPluginTest.java new file mode 100644 index 0000000000..bd78e669fd --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/plugins/DstHelper/DstHelperPluginTest.java @@ -0,0 +1,65 @@ +package info.nightscout.androidaps.plugins.DstHelper; + +import android.content.Context; + +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import junit.framework.Assert; + +import info.AAPSMocker; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.SP; +import info.nightscout.utils.T; + +import org.junit.Test; + +import java.util.Calendar; +import java.util.TimeZone; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, SP.class, Context.class}) +public class DstHelperPluginTest { + DstHelperPlugin plugin = new DstHelperPlugin(); + @Test + public void runTest() throws Exception { + AAPSMocker.mockMainApp(); + AAPSMocker.mockApplicationContext(); + // test different time zones + //Starting with Europe/Sofia + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("Europe/Sofia")); + c.setTimeInMillis(DateUtil.fromISODateString("2018-10-28T02:00:00Z").getTime()); + int minutesLeftToChange = plugin.dstTest(c); + Assert.assertEquals(60, minutesLeftToChange); + c.setTimeInMillis(DateUtil.fromISODateString("2018-03-25T02:00:00Z").getTime()); + minutesLeftToChange = plugin.dstTest(c); + Assert.assertEquals(60, minutesLeftToChange); + // try something with half hour somewhere in Australia + c = Calendar.getInstance(TimeZone.getTimeZone("Australia/Lord_Howe")); + c.setTimeInMillis(DateUtil.fromISODateString("2018-04-01T00:00:00Z").getTime()); + minutesLeftToChange = plugin.dstTest(c); + // try something with half hour somewhere in Australia + c = Calendar.getInstance(TimeZone.getTimeZone("Australia/Lord_Howe")); + c.setTimeInMillis(DateUtil.fromISODateString("2018-04-01T00:00:00Z").getTime()); + minutesLeftToChange = plugin.dstTest(c); + Assert.assertEquals(90, minutesLeftToChange); + c = Calendar.getInstance(TimeZone.getTimeZone("Australia/Lord_Howe")); + // and back + c.setTimeInMillis(DateUtil.fromISODateString("2018-10-07T00:00:00Z").getTime()); + minutesLeftToChange = plugin.dstTest(c); + Assert.assertEquals(120, minutesLeftToChange); + + c.setTimeInMillis(DateUtil.fromISODateString("2018-10-08T00:00:00Z").getTime()); + minutesLeftToChange = plugin.dstTest(c); + Assert.assertEquals(0, minutesLeftToChange); + + // DST event was 30 mins + c.setTimeInMillis(DateUtil.fromISODateString("2018-04-01T02:00:00Z").getTime()); + minutesLeftToChange = plugin.dstTest(c); +// Assert.assertEquals(630, plugin.zoneOffsetInMinutes(c)); + Assert.assertEquals(0, minutesLeftToChange); + } + +} \ No newline at end of file