diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.java new file mode 100644 index 0000000000..2dfbf9ae35 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.java @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.events; + +/** Base class for events to update the UI, mostly a specific tab. */ +public class EventAcceptOpenLoopChange extends Event { +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index d82b010450..c9fdb45f86 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -44,6 +44,7 @@ import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; import info.nightscout.androidaps.plugins.ConfigBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.Loop.events.EventLoopSetLastRunGui; @@ -51,6 +52,8 @@ import info.nightscout.androidaps.plugins.Loop.events.EventLoopUpdateGui; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; import info.nightscout.androidaps.plugins.NSClientInternal.NSUpload; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.androidaps.plugins.Wear.ActionStringHandler; +import info.nightscout.androidaps.events.EventAcceptOpenLoopChange; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.FabricPrivacy; @@ -396,7 +399,8 @@ public class LoopPlugin extends PluginBase { .setAutoCancel(true) .setPriority(Notification.PRIORITY_HIGH) .setCategory(Notification.CATEGORY_ALARM) - .setVisibility(Notification.VISIBILITY_PUBLIC); + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true); // Creates an explicit intent for an Activity in your app Intent resultIntent = new Intent(MainApp.instance().getApplicationContext(), MainActivity.class); @@ -418,6 +422,15 @@ public class LoopPlugin extends PluginBase { // mId allows you to update the notification later on. mNotificationManager.notify(Constants.notificationID, builder.build()); MainApp.bus().post(new EventNewOpenLoopNotification()); + + // Send to Wear + ActionStringHandler.handleInitiate("changeRequest"); + } else if (allowNotification) { + // dismiss notifications + NotificationManager notificationManager = + (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(Constants.notificationID); + ActionStringHandler.handleInitiate("cancelChangeRequest"); } } @@ -428,6 +441,29 @@ public class LoopPlugin extends PluginBase { } } + public void acceptChangeRequest() { + Profile profile = ProfileFunctions.getInstance().getProfile(); + + applyTBRRequest(lastRun.constraintsProcessed, profile, new Callback() { + @Override + public void run() { + if (result.enacted) { + lastRun.tbrSetByPump = result; + lastRun.lastEnact = new Date(); + lastRun.lastOpenModeAccept = new Date(); + NSUpload.uploadDeviceStatus(); + ObjectivesPlugin objectivesPlugin = MainApp.getSpecificPlugin(ObjectivesPlugin.class); + if (objectivesPlugin != null) { + ObjectivesPlugin.manualEnacts++; + ObjectivesPlugin.saveProgress(); + } + } + MainApp.bus().post(new EventAcceptOpenLoopChange()); + } + }); + FabricPrivacy.getInstance().logCustom(new CustomEvent("AcceptTemp")); + } + /** * expect absolute request and allow both absolute and percent response based on pump capabilities */ diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index 6fc1e8d5dd..3697bbd1a0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -110,6 +110,8 @@ import info.nightscout.androidaps.plugins.Source.SourceDexcomG5Plugin; 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.plugins.Wear.ActionStringHandler; +import info.nightscout.androidaps.events.EventAcceptOpenLoopChange; import info.nightscout.androidaps.queue.Callback; import info.nightscout.utils.BolusWizard; import info.nightscout.utils.DateUtil; @@ -741,24 +743,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { hideTempRecommendation(); clearNotification(); - LoopPlugin.getPlugin().applyTBRRequest(finalLastRun.constraintsProcessed, profile, new Callback() { - @Override - public void run() { - if (result.enacted) { - finalLastRun.tbrSetByPump = result; - finalLastRun.lastEnact = new Date(); - finalLastRun.lastOpenModeAccept = new Date(); - NSUpload.uploadDeviceStatus(); - ObjectivesPlugin objectivesPlugin = MainApp.getSpecificPlugin(ObjectivesPlugin.class); - if (objectivesPlugin != null) { - ObjectivesPlugin.manualEnacts++; - ObjectivesPlugin.saveProgress(); - } - } - scheduleUpdateGUI("onClickAcceptTemp"); - } - }); - FabricPrivacy.getInstance().logCustom(new CustomEvent("AcceptTemp")); + LoopPlugin.getPlugin().acceptChangeRequest(); }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); @@ -958,6 +943,11 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, scheduleUpdateGUI("EventNewOpenLoopNotification"); } + @Subscribe + public void onStatusEvent(final EventAcceptOpenLoopChange ev) { + scheduleUpdateGUI("EventAcceptOpenLoopChange"); + } + @Subscribe public void onStatusEvent(final EventTempTargetChange ev) { scheduleUpdateGUI("EventTempTargetChange"); @@ -998,6 +988,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, NotificationManager notificationManager = (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(Constants.notificationID); + + ActionStringHandler.handleInitiate("cancelChangeRequest"); } private void updatePumpStatus(String status) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java index 84e32ca087..0fd1c557b1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java @@ -1,5 +1,7 @@ package info.nightscout.androidaps.plugins.Wear; +import android.app.NotificationManager; +import android.content.Context; import android.os.HandlerThread; import android.support.annotation.NonNull; @@ -353,6 +355,23 @@ public class ActionStringHandler { } rAction += "ecarbs " + carbsAfterConstraints + " " + starttimestamp + " " + duration; + } else if ("changeRequest".equals(act[0])) { + ////////////////////////////////////////////// CHANGE REQUEST + rTitle = MainApp.gs(R.string.openloop_newsuggestion); + rAction = "changeRequest"; + final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; + rMessage += finalLastRun.constraintsProcessed; + + WearPlugin.getPlugin().requestChangeConfirmation(rTitle, rMessage, rAction); + lastSentTimestamp = System.currentTimeMillis(); + lastConfirmActionString = rAction; + return; + } else if ("cancelChangeRequest".equals(act[0])) { + ////////////////////////////////////////////// CANCEL CHANGE REQUEST NOTIFICATION + rAction = "cancelChangeRequest"; + + WearPlugin.getPlugin().requestNotificationCancel(rAction); + return; } else return; @@ -626,6 +645,11 @@ public class ActionStringHandler { doECarbs(carbs, starttime, duration); } else if ("dismissoverviewnotification".equals(act[0])) { MainApp.bus().post(new EventDismissNotification(SafeParse.stringToInt(act[1]))); + } else if ("changeRequest".equals(act[0])) { + LoopPlugin.getPlugin().acceptChangeRequest(); + NotificationManager notificationManager = + (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(Constants.notificationID); } lastBolusWizard = null; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java index 6cb47e1a3a..72b2397213 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java @@ -100,6 +100,12 @@ public class WearPlugin extends PluginBase { ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS)); } + void requestNotificationCancel(String actionstring) { + Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION); + intent.putExtra("actionstring", actionstring); + ctx.startService(intent); + } + @Subscribe public void onStatusEvent(final EventPreferenceChange ev) { @@ -192,6 +198,15 @@ public class WearPlugin extends PluginBase { ctx.startService(intent); } + public void requestChangeConfirmation(String title, String message, String actionstring) { + + Intent intent = new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_CHANGECONFIRMATIONREQUEST); + intent.putExtra("title", title); + intent.putExtra("message", message); + intent.putExtra("actionstring", actionstring); + ctx.startService(intent); + } + public static void registerWatchUpdaterService(WatchUpdaterService wus) { watchUS = wus; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java index ce9b0e0f2f..4b4d1c4854 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java @@ -64,7 +64,8 @@ public class WatchUpdaterService extends WearableListenerService implements public static final String ACTION_SEND_BASALS = WatchUpdaterService.class.getName().concat(".SendBasals"); public static final String ACTION_SEND_BOLUSPROGRESS = WatchUpdaterService.class.getName().concat(".BolusProgress"); public static final String ACTION_SEND_ACTIONCONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat(".ActionConfirmationRequest"); - + public static final String ACTION_SEND_CHANGECONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat(".ChangeConfirmationRequest"); + public static final String ACTION_CANCEL_NOTIFICATION = WatchUpdaterService.class.getName().concat(".CancelNotification"); private GoogleApiClient googleApiClient; public static final String WEARABLE_DATA_PATH = "/nightscout_watch_data"; @@ -79,6 +80,8 @@ public class WatchUpdaterService extends WearableListenerService implements public static final String BASAL_DATA_PATH = "/nightscout_watch_basal"; public static final String BOLUS_PROGRESS_PATH = "/nightscout_watch_bolusprogress"; public static final String ACTION_CONFIRMATION_REQUEST_PATH = "/nightscout_watch_actionconfirmationrequest"; + public static final String ACTION_CHANGECONFIRMATION_REQUEST_PATH = "/nightscout_watch_changeconfirmationrequest"; + public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest"; boolean wear_integration = false; @@ -154,6 +157,14 @@ public class WatchUpdaterService extends WearableListenerService implements String message = intent.getStringExtra("message"); String actionstring = intent.getStringExtra("actionstring"); sendActionConfirmationRequest(title, message, actionstring); + } else if (ACTION_SEND_CHANGECONFIRMATIONREQUEST.equals(action)) { + String title = intent.getStringExtra("title"); + String message = intent.getStringExtra("message"); + String actionstring = intent.getStringExtra("actionstring"); + sendChangeConfirmationRequest(title, message, actionstring); + } else if (ACTION_CANCEL_NOTIFICATION.equals(action)) { + String actionstring = intent.getStringExtra("actionstring"); + sendCancelNotificationRequest(actionstring); } else { sendData(); } @@ -578,6 +589,42 @@ public class WatchUpdaterService extends WearableListenerService implements } } + private void sendChangeConfirmationRequest(String title, String message, String actionstring) { + if (googleApiClient.isConnected()) { + PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CHANGECONFIRMATION_REQUEST_PATH); + //unique content + dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); + dataMapRequest.getDataMap().putString("changeConfirmationRequest", "changeConfirmationRequest"); + dataMapRequest.getDataMap().putString("title", title); + dataMapRequest.getDataMap().putString("message", message); + dataMapRequest.getDataMap().putString("actionstring", actionstring); + + log.debug("Requesting confirmation from wear: " + actionstring); + + PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); + } else { + Log.e("changeConfirmRequest", "No connection to wearable available!"); + } + } + + private void sendCancelNotificationRequest(String actionstring) { + if (googleApiClient.isConnected()) { + PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CANCELNOTIFICATION_REQUEST_PATH); + //unique content + dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); + dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest"); + dataMapRequest.getDataMap().putString("actionstring", actionstring); + + log.debug("Canceling notification on wear: " + actionstring); + + PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); + } else { + Log.e("cancelNotificationRequest", "No connection to wearable available!"); + } + } + private void sendStatus() { if (googleApiClient.isConnected()) { diff --git a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java index 7297d51815..52dfc97ad1 100644 --- a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java +++ b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java @@ -1,5 +1,7 @@ package info.nightscout.androidaps.data; +import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -49,14 +51,18 @@ public class ListenerService extends WearableListenerService implements GoogleAp public static final String BASAL_DATA_PATH = "/nightscout_watch_basal"; public static final String BOLUS_PROGRESS_PATH = "/nightscout_watch_bolusprogress"; public static final String ACTION_CONFIRMATION_REQUEST_PATH = "/nightscout_watch_actionconfirmationrequest"; + public static final String NEW_CHANGECONFIRMATIONREQUEST_PATH = "/nightscout_watch_changeconfirmationrequest"; + public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest"; public static final int BOLUS_PROGRESS_NOTIF_ID = 001; public static final int CONFIRM_NOTIF_ID = 002; + public static final int CHANGE_NOTIF_ID = 556677; private static final String ACTION_RESEND = "com.dexdrip.stephenblack.nightwatch.RESEND_DATA"; private static final String ACTION_CANCELBOLUS = "com.dexdrip.stephenblack.nightwatch.CANCELBOLUS"; private static final String ACTION_CONFIRMATION = "com.dexdrip.stephenblack.nightwatch.CONFIRMACTION"; + private static final String ACTION_CONFIRMCHANGE = "com.dexdrip.stephenblack.nightwatch.CONFIRMCHANGE"; private static final String ACTION_INITIATE_ACTION = "com.dexdrip.stephenblack.nightwatch.INITIATE_ACTION"; @@ -223,6 +229,17 @@ public class ListenerService extends WearableListenerService implements GoogleAp String actionstring = intent.getStringExtra("actionstring"); sendConfirmActionstring(actionstring); + } else if(intent != null && ACTION_CONFIRMCHANGE.equals(intent.getAction())){ + googleApiConnect(); + + //dismiss notification + NotificationManagerCompat notificationManager = + NotificationManagerCompat.from(ListenerService.this); + notificationManager.cancel(CHANGE_NOTIF_ID); + + String actionstring = intent.getStringExtra("actionstring"); + sendConfirmActionstring(actionstring); + } else if(intent != null && ACTION_INITIATE_ACTION.equals(intent.getAction())){ googleApiConnect(); @@ -273,7 +290,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp showConfirmationDialog(title, message, actionstring); } - }else if (path.equals(NEW_STATUS_PATH)) { + } else if (path.equals(NEW_STATUS_PATH)) { dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); Intent messageIntent = new Intent(); messageIntent.setAction(Intent.ACTION_SEND); @@ -294,6 +311,14 @@ public class ListenerService extends WearableListenerService implements GoogleAp editor.putBoolean("wearcontrol", wearcontrol); editor.commit(); } + } else if (path.equals(NEW_CHANGECONFIRMATIONREQUEST_PATH)) { + String title = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("title"); + String message = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("message"); + String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("actionstring"); + notifyChangeRequest(title, message, actionstring); + } else if (path.equals(ACTION_CANCELNOTIFICATION_REQUEST_PATH)) { + String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("actionstring"); + cancelNotificationRequest(actionstring); } else { dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); Intent messageIntent = new Intent(); @@ -305,6 +330,41 @@ public class ListenerService extends WearableListenerService implements GoogleAp } } + private void notifyChangeRequest(String title, String message, String actionstring) { + + Notification.Builder builder = + new Notification.Builder(this); //,"AndroidAPS-Openloop"); + builder.setSmallIcon(R.drawable.notif_icon) + .setContentTitle(title) + .setContentText(message) + .setPriority(Notification.PRIORITY_HIGH); + + // Creates an explicit intent for an Activity in your app + Intent intent = new Intent(this, AcceptActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Bundle params = new Bundle(); + params.putString("title", title); + params.putString("message", message); + params.putString("actionstring", actionstring); + intent.putExtras(params); + + PendingIntent resultPendingIntent = + PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + builder.setContentIntent(resultPendingIntent); + builder.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}); + + NotificationManager mNotificationManager = + (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + // mId allows you to update the notification later on. + mNotificationManager.notify(CHANGE_NOTIF_ID, builder.build()); + } + + private void cancelNotificationRequest(String actionstring) { + NotificationManager mNotificationManager = + (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + mNotificationManager.cancel(CHANGE_NOTIF_ID); + } + private void showBolusProgress(int progresspercent, String progresstatus) { Intent cancelIntent = new Intent(this, ListenerService.class); cancelIntent.setAction(ACTION_CANCELBOLUS); @@ -409,7 +469,12 @@ public class ListenerService extends WearableListenerService implements GoogleAp public static void confirmAction(Context context, String actionstring) { Intent intent = new Intent(context, ListenerService.class); intent.putExtra("actionstring", actionstring); - intent.setAction(ACTION_CONFIRMATION); + + if (actionstring.equals("changeRequest")) { + intent.setAction(ACTION_CONFIRMCHANGE); + } else { + intent.setAction(ACTION_CONFIRMATION); + } context.startService(intent); } diff --git a/wear/src/main/res/drawable/notif_icon.png b/wear/src/main/res/drawable/notif_icon.png new file mode 100644 index 0000000000..480d0eaf77 Binary files /dev/null and b/wear/src/main/res/drawable/notif_icon.png differ