package info.nightscout.androidaps.plugins.Loop; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.HandlerThread; import android.support.v7.app.NotificationCompat; import com.squareup.otto.Subscribe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainActivity; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Loop.events.EventLoopSetLastRunGui; import info.nightscout.androidaps.plugins.Loop.events.EventLoopUpdateGui; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; import info.nightscout.utils.SP; /** * Created by mike on 05.08.2016. */ public class LoopPlugin implements PluginBase { private static Logger log = LoggerFactory.getLogger(LoopPlugin.class); private static Handler sHandler; private static HandlerThread sHandlerThread; private boolean fragmentEnabled = false; private boolean fragmentVisible = true; private long loopSuspendedTill = 0L; // end of manual loop suspend public class LastRun { public APSResult request = null; public APSResult constraintsProcessed = null; public PumpEnactResult setByPump = null; public String source = null; public Date lastAPSRun = null; public Date lastEnact = null; public Date lastOpenModeAccept; } static public LastRun lastRun = null; public LoopPlugin() { if (sHandlerThread == null) { sHandlerThread = new HandlerThread(LoopPlugin.class.getSimpleName()); sHandlerThread.start(); sHandler = new Handler(sHandlerThread.getLooper()); } MainApp.bus().register(this); loopSuspendedTill = SP.getLong("loopSuspendedTill", 0L); } @Override public String getFragmentClass() { return LoopFragment.class.getName(); } @Override public int getType() { return PluginBase.LOOP; } @Override public String getName() { return MainApp.instance().getString(R.string.loop); } @Override public String getNameShort() { String name = MainApp.sResources.getString(R.string.loop_shortname); if (!name.trim().isEmpty()){ //only if translation exists return name; } // use long name as fallback return getName(); } @Override public boolean isEnabled(int type) { return type == LOOP && fragmentEnabled && MainApp.getConfigBuilder().getPumpDescription().isTempBasalCapable; } @Override public boolean isVisibleInTabs(int type) { return type == LOOP && fragmentVisible && MainApp.getConfigBuilder().getPumpDescription().isTempBasalCapable; } @Override public boolean canBeHidden(int type) { return true; } @Override public void setFragmentEnabled(int type, boolean fragmentEnabled) { if (type == LOOP) this.fragmentEnabled = fragmentEnabled; } @Override public void setFragmentVisible(int type, boolean fragmentVisible) { if (type == LOOP) this.fragmentVisible = fragmentVisible; } @Subscribe public void onStatusEvent(final EventTreatmentChange ev) { invoke("EventTreatmentChange", true); } @Subscribe public void onStatusEvent(final EventNewBG ev) { invoke("EventNewBG", true); } public long suspendedTo() { return loopSuspendedTill; } public void suspendTo(long endTime) { loopSuspendedTill = endTime; SP.putLong("loopSuspendedTill", loopSuspendedTill); } public int minutesToEndOfSuspend() { if (loopSuspendedTill == 0) return 0; long now = new Date().getTime(); long msecDiff = loopSuspendedTill - now; if (loopSuspendedTill <= now) { // time exceeded suspendTo(0L); return 0; } return (int) (msecDiff / 60d / 1000d); } public boolean isSuspended() { if (loopSuspendedTill == 0) return false; long now = new Date().getTime(); if (loopSuspendedTill <= now) { // time exceeded suspendTo(0L); return false; } return true; } public void invoke(String initiator, boolean allowNotification) { try { if (Config.logFunctionCalls) log.debug("invoke"); ConstraintsInterface constraintsInterface = MainApp.getConfigBuilder(); if (!constraintsInterface.isLoopEnabled()) { log.debug(MainApp.sResources.getString(R.string.loopdisabled)); MainApp.bus().post(new EventLoopSetLastRunGui(MainApp.sResources.getString(R.string.loopdisabled))); return; } final ConfigBuilderPlugin configBuilder = MainApp.getConfigBuilder(); APSResult result = null; if (configBuilder == null || !isEnabled(PluginBase.LOOP)) return; if (isSuspended()) { log.debug(MainApp.sResources.getString(R.string.loopsuspended)); MainApp.bus().post(new EventLoopSetLastRunGui(MainApp.sResources.getString(R.string.loopsuspended))); return; } if (configBuilder.isSuspended()) { log.debug(MainApp.sResources.getString(R.string.pumpsuspended)); MainApp.bus().post(new EventLoopSetLastRunGui(MainApp.sResources.getString(R.string.pumpsuspended))); return; } // Check if pump info is loaded if (configBuilder.getBaseBasalRate() < 0.01d) return; APSInterface usedAPS = configBuilder.getActiveAPS(); if (usedAPS != null && ((PluginBase) usedAPS).isEnabled(PluginBase.APS)) { usedAPS.invoke(initiator); result = usedAPS.getLastAPSResult(); } // Check if we have any result if (result == null) { MainApp.bus().post(new EventLoopSetLastRunGui(MainApp.sResources.getString(R.string.noapsselected))); return; } // check rate for constrais final APSResult resultAfterConstraints = result.clone(); resultAfterConstraints.rate = constraintsInterface.applyBasalConstraints(resultAfterConstraints.rate); if (lastRun == null) lastRun = new LastRun(); lastRun.request = result; lastRun.constraintsProcessed = resultAfterConstraints; lastRun.lastAPSRun = new Date(); lastRun.source = ((PluginBase) usedAPS).getName(); lastRun.setByPump = null; if (constraintsInterface.isClosedModeEnabled()) { if (result.changeRequested) { final PumpEnactResult waiting = new PumpEnactResult(); final PumpEnactResult previousResult = lastRun.setByPump; waiting.queued = true; lastRun.setByPump = waiting; MainApp.bus().post(new EventLoopUpdateGui()); sHandler.post(new Runnable() { @Override public void run() { final PumpEnactResult applyResult = configBuilder.applyAPSRequest(resultAfterConstraints); if (applyResult.enacted || applyResult.success) { lastRun.setByPump = applyResult; lastRun.lastEnact = lastRun.lastAPSRun; } else { lastRun.setByPump = previousResult; } MainApp.bus().post(new EventLoopUpdateGui()); } }); } else { lastRun.setByPump = null; lastRun.source = null; } } else { if (result.changeRequested && allowNotification) { NotificationCompat.Builder builder = new NotificationCompat.Builder(MainApp.instance().getApplicationContext()); builder.setSmallIcon(R.drawable.notif_icon) .setContentTitle(MainApp.sResources.getString(R.string.openloop_newsuggestion)) .setContentText(resultAfterConstraints.toString()) .setAutoCancel(true) .setPriority(Notification.PRIORITY_HIGH) .setCategory(Notification.CATEGORY_ALARM) .setVisibility(Notification.VISIBILITY_PUBLIC); // Creates an explicit intent for an Activity in your app Intent resultIntent = new Intent(MainApp.instance().getApplicationContext(), MainActivity.class); // The stack builder object will contain an artificial back stack for the // started Activity. // This ensures that navigating backward from the Activity leads out of // your application to the Home screen. TaskStackBuilder stackBuilder = TaskStackBuilder.create(MainApp.instance().getApplicationContext()); stackBuilder.addParentStack(MainActivity.class); // Adds the Intent that starts the Activity to the top of the stack stackBuilder.addNextIntent(resultIntent); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(resultPendingIntent); builder.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}); NotificationManager mNotificationManager = (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); // mId allows you to update the notification later on. mNotificationManager.notify(Constants.notificationID, builder.build()); MainApp.bus().post(new EventNewOpenLoopNotification()); } } MainApp.bus().post(new EventLoopUpdateGui()); MainApp.getConfigBuilder().uploadDeviceStatus(); } finally { if (Config.logFunctionCalls) log.debug("invoke end"); } } }