AutomationPlugin:processActions and some tweaking

This commit is contained in:
Milos Kozak 2019-03-31 21:54:31 +02:00
parent 350e23647e
commit 5a555cdfba
19 changed files with 255 additions and 29 deletions

View file

@ -20,6 +20,8 @@ import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.squareup.otto.Subscribe;
import java.util.HashSet;
import java.util.List;
@ -33,6 +35,7 @@ import info.nightscout.androidaps.plugins.general.automation.actions.Action;
import info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseTriggerDialog;
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditActionDialog;
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog;
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
@ -40,6 +43,8 @@ public class AutomationFragment extends SubscriberFragment {
@BindView(R.id.eventListView)
RecyclerView mEventListView;
@BindView(R.id.logView)
TextView mLogView;
private EventListAdapter mEventListAdapter;
@ -62,11 +67,24 @@ public class AutomationFragment extends SubscriberFragment {
return view;
}
@Subscribe
public void onEvent(EventAutomationUpdateGui unused) {
updateGUI();
}
@Override
public void updateGUI() {
Activity activity = getActivity();
if (activity != null)
activity.runOnUiThread(() -> mEventListAdapter.notifyDataSetChanged());
activity.runOnUiThread(() -> {
mEventListAdapter.notifyDataSetChanged();
StringBuilder sb = new StringBuilder();
for (String l : AutomationPlugin.getPlugin().executionLog) {
sb.append(l);
sb.append("\n");
}
mLogView.setText(sb.toString());
});
}
@OnClick(R.id.fabAddEvent)
@ -78,7 +96,7 @@ public class AutomationFragment extends SubscriberFragment {
/**
* RecyclerViewAdapter to display event lists.
*/
public static class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
public static class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
private final List<AutomationEvent> mEventList;
private final FragmentManager mFragmentManager;
@ -97,7 +115,7 @@ public class AutomationFragment extends SubscriberFragment {
private void addImage(@DrawableRes int res, Context context, LinearLayout layout) {
ImageView iv = new ImageView(context);
iv.setImageResource(res);
iv.setLayoutParams(new LinearLayout.LayoutParams(MainApp.dpToPx(24),MainApp.dpToPx(24)));
iv.setLayoutParams(new LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)));
layout.addView(iv);
}
@ -109,25 +127,25 @@ public class AutomationFragment extends SubscriberFragment {
// trigger icons
HashSet<Integer> triggerIcons = new HashSet<>();
TriggerConnector.fillIconSet((TriggerConnector)event.getTrigger(), triggerIcons);
for(int res : triggerIcons) {
TriggerConnector.fillIconSet((TriggerConnector) event.getTrigger(), triggerIcons);
for (int res : triggerIcons) {
addImage(res, holder.context, holder.iconLayout);
}
// arrow icon
ImageView iv = new ImageView(holder.context);
iv.setImageResource(R.drawable.ic_arrow_forward_white_24dp);
iv.setLayoutParams(new LinearLayout.LayoutParams(MainApp.dpToPx(24),MainApp.dpToPx(24)));
iv.setLayoutParams(new LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)));
iv.setPadding(MainApp.dpToPx(4), 0, MainApp.dpToPx(4), 0);
holder.iconLayout.addView(iv);
// action icons
HashSet<Integer> actionIcons = new HashSet<>();
for(Action action : event.getActions()) {
for (Action action : event.getActions()) {
if (action.icon().isPresent())
actionIcons.add(action.icon().get());
}
for(int res : actionIcons) {
for (int res : actionIcons) {
addImage(res, holder.context, holder.iconLayout);
}
@ -162,7 +180,7 @@ public class AutomationFragment extends SubscriberFragment {
eventTitle = view.findViewById(R.id.viewEventTitle);
rootLayout = view.findViewById(R.id.rootLayout);
iconLayout = view.findViewById(R.id.iconLayout);
iconTrash = view.findViewById(R.id.iconTrash);
iconTrash = view.findViewById(R.id.iconTrash);
}
}
}
@ -170,7 +188,7 @@ public class AutomationFragment extends SubscriberFragment {
/**
* RecyclerViewAdapter to display action lists.
*/
public static class ActionListAdapter extends RecyclerView.Adapter<ActionListAdapter.ViewHolder> {
public static class ActionListAdapter extends RecyclerView.Adapter<ActionListAdapter.ViewHolder> {
private final List<Action> mActionList;
private final FragmentManager mFragmentManager;
@ -257,7 +275,7 @@ public class AutomationFragment extends SubscriberFragment {
}
private void build() {
for(int i = 0; i < mRootConnector.size(); ++i) {
for (int i = 0; i < mRootConnector.size(); ++i) {
final Trigger trigger = mRootConnector.get(i);
// spinner
@ -317,7 +335,8 @@ public class AutomationFragment extends SubscriberFragment {
}
@Override
public void onNothingSelected(AdapterView<?> parent) { }
public void onNothingSelected(AdapterView<?> parent) {
}
});
mRootLayout.addView(spinner);
}
@ -352,7 +371,7 @@ public class AutomationFragment extends SubscriberFragment {
dialog.show(mFragmentManager, "ChooseTriggerDialog");
dialog.setOnClickListener(newTriggerObject -> {
TriggerConnector connector = trigger.getConnector();
connector.add(connector.pos(trigger)+1, newTriggerObject);
connector.add(connector.pos(trigger) + 1, newTriggerObject);
connector.simplify().rebuildView();
});
});
@ -363,7 +382,7 @@ public class AutomationFragment extends SubscriberFragment {
buttonCopy.setText("copy");
buttonCopy.setOnClickListener(v -> {
TriggerConnector connector = trigger.getConnector();
connector.add(connector.pos(trigger)+1, trigger.duplicate());
connector.add(connector.pos(trigger) + 1, trigger.duplicate());
connector.simplify().rebuildView();
});
buttonLayout.addView(buttonCopy);
@ -377,7 +396,7 @@ public class AutomationFragment extends SubscriberFragment {
TriggerConnector newConnector = new TriggerConnector(newConnectorType);
// move trigger from pos and pos+1 into new connector
for(int i = 0; i < 2; ++i) {
for (int i = 0; i < 2; ++i) {
Trigger t = connector.get(pos);
newConnector.add(t);
connector.remove(t);

View file

@ -2,10 +2,16 @@ package info.nightscout.androidaps.plugins.general.automation;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Handler;
import com.squareup.otto.Subscribe;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
@ -18,11 +24,21 @@ import info.nightscout.androidaps.events.EventPreferenceChange;
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.general.automation.actions.Action;
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged;
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.services.LocationService;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.T;
public class AutomationPlugin extends PluginBase {
private static Logger log = LoggerFactory.getLogger(L.CORE);
private final String key_AUTOMATION_EVENTS = "AUTOMATION_EVENTS";
static AutomationPlugin plugin = null;
public static AutomationPlugin getPlugin() {
@ -35,6 +51,16 @@ public class AutomationPlugin extends PluginBase {
private EventLocationChange eventLocationChange;
private EventChargingState eventChargingState;
private EventNetworkChange eventNetworkChange;
List<String> executionLog = new ArrayList<>();
private Handler loopHandler = new Handler();
private Runnable refreshLoop = new Runnable() {
@Override
public void run() {
processActions();
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs());
}
};
private AutomationPlugin() {
super(new PluginDescription()
@ -54,10 +80,13 @@ public class AutomationPlugin extends PluginBase {
MainApp.bus().register(this);
super.onStart();
loadFromSP();
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs());
}
@Override
protected void onStop() {
loopHandler.removeCallbacks(refreshLoop);
Context context = MainApp.instance().getApplicationContext();
context.stopService(new Intent(context, LocationService.class));
@ -80,6 +109,36 @@ public class AutomationPlugin extends PluginBase {
return eventNetworkChange;
}
private void storeToSP() {
JSONArray array = new JSONArray();
try {
for (AutomationEvent event : getAutomationEvents()) {
array.put(new JSONObject(event.toJSON()));
}
} catch (JSONException e) {
e.printStackTrace();
}
SP.putString(key_AUTOMATION_EVENTS, array.toString());
}
private void loadFromSP() {
automationEvents.clear();
String data = SP.getString(key_AUTOMATION_EVENTS, "");
if (!data.equals("")) {
try {
JSONArray array = new JSONArray(data);
for (int i = 0; i < array.length(); i++) {
JSONObject o = array.getJSONObject(i);
AutomationEvent event = new AutomationEvent().fromJSON(o.toString());
automationEvents.add(event);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
@Subscribe
public void onEventPreferenceChange(EventPreferenceChange e) {
if (e.isChanged(R.string.key_location)) {
@ -89,6 +148,11 @@ public class AutomationPlugin extends PluginBase {
}
}
@Subscribe
public void onEvent(EventAutomationDataChanged e) {
storeToSP();
}
@Subscribe
public void onEventLocationChange(EventLocationChange e) {
eventLocationChange = e;
@ -112,9 +176,33 @@ public class AutomationPlugin extends PluginBase {
processActions();
}
// TODO keepalive
void processActions() {
synchronized void processActions() {
log.debug("processActions");
for (AutomationEvent event : getAutomationEvents()) {
if (event.getTrigger().shouldRun()) {
List<Action> actions = event.getActions();
for (Action action : actions) {
action.doAction(new Callback() {
@Override
public void run() {
StringBuilder sb = new StringBuilder();
sb.append(DateUtil.timeString(DateUtil.now()));
sb.append(" ");
sb.append(result.success ? "" : "X");
sb.append(" ");
sb.append(event.getTitle());
sb.append(": ");
sb.append(action.shortDescription());
sb.append(": ");
sb.append(result.comment);
executionLog.add(sb.toString());
log.debug(sb.toString());
MainApp.bus().post(new EventAutomationUpdateGui());
}
});
}
}
}
}
}

View file

@ -9,11 +9,43 @@ import org.json.JSONObject;
import info.nightscout.androidaps.queue.Callback;
/*
Action ideas:
* cancel temp target
* change preference setting
* enable/disable plugin
* create notification
* create ugly alarm
* create profile switch
* set/cancel tbr
* set/cancel extended bolus
* run bolus wizard
Trigger ideas:
* location (close to)
* connected to specific wifi
* internet available/not available
* nsclient connected/disconnected
* iob
* cob
* autosens value
* delta, short delta, long delta
* last bolus ago
* is tbr running
* bolus wizard result
* loop is enabled, disabled, suspended, running
*/
public abstract class Action {
public abstract int friendlyName();
public abstract String shortDescription();
abstract void doAction(Callback callback);
public abstract void doAction(Callback callback);
public void generateDialog(LinearLayout root) { }

View file

@ -18,7 +18,12 @@ public class ActionLoopDisable extends Action {
}
@Override
void doAction(Callback callback) {
public String shortDescription() {
return MainApp.gs(R.string.disableloop);
}
@Override
public void doAction(Callback callback) {
if (LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, false);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopDisable");

View file

@ -18,7 +18,12 @@ public class ActionLoopEnable extends Action {
}
@Override
void doAction(Callback callback) {
public String shortDescription() {
return MainApp.gs(R.string.enableloop);
}
@Override
public void doAction(Callback callback) {
if (!LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, true);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopEnable");

View file

@ -18,7 +18,12 @@ public class ActionLoopResume extends Action {
}
@Override
void doAction(Callback callback) {
public String shortDescription() {
return MainApp.gs(R.string.resumeloop);
}
@Override
public void doAction(Callback callback) {
if (LoopPlugin.getPlugin().isSuspended()) {
LoopPlugin.getPlugin().suspendTo(0);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopResume");

View file

@ -21,7 +21,12 @@ public class ActionLoopSuspend extends Action {
}
@Override
void doAction(Callback callback) {
public String shortDescription() {
return MainApp.gs(R.string.suspendloop);
}
@Override
public void doAction(Callback callback) {
if (!LoopPlugin.getPlugin().isSuspended()) {
LoopPlugin.getPlugin().suspendLoop(minutes);
MainApp.bus().post(new EventRefreshOverview("ActionLoopSuspend"));

View file

@ -26,6 +26,7 @@ public class ActionStartTempTarget extends Action {
String reason = "";
InputBg value;
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
TempTarget tempTarget;
public ActionStartTempTarget() {
value = new InputBg(Constants.MGDL);
@ -41,8 +42,13 @@ public class ActionStartTempTarget extends Action {
}
@Override
void doAction(Callback callback) {
TempTarget tempTarget = new TempTarget().date(DateUtil.now()).duration((int)duration.getMinutes()).reason(reason).source(Source.USER).low(value.getMgdl()).high(value.getMgdl());
public String shortDescription() {
return MainApp.gs(R.string.resumeloop) + ": " + tempTarget.toString();
}
@Override
public void doAction(Callback callback) {
tempTarget = new TempTarget().date(DateUtil.now()).duration((int)duration.getMinutes()).reason(reason).source(Source.USER).low(value.getMgdl()).high(value.getMgdl());
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();

View file

@ -16,10 +16,12 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.general.automation.AutomationEvent;
import info.nightscout.androidaps.plugins.general.automation.AutomationFragment;
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
public class EditEventDialog extends DialogFragment {
@ -82,6 +84,7 @@ public class EditEventDialog extends DialogFragment {
mAddNew = savedInstanceState.getBoolean("addNew");
} else if (mAddNew) {
mEvent.setTrigger(new TriggerConnector(TriggerConnector.Type.OR));
}
// event title
@ -159,6 +162,7 @@ public class EditEventDialog extends DialogFragment {
if (mClickListener != null) mClickListener.onClick(mEvent);
dismiss();
MainApp.bus().post(new EventAutomationDataChanged());
}
@OnClick(R.id.cancel)

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.events;
import info.nightscout.androidaps.events.Event;
public class EventAutomationDataChanged extends Event {
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.events;
import info.nightscout.androidaps.events.Event;
public class EventAutomationUpdateGui extends Event {
}

View file

@ -106,11 +106,19 @@ public class DateUtil {
}
public static String timeString(Date date) {
return new DateTime(date).toString(DateTimeFormat.shortTime());
String format = "hh:mma";
if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) {
format = "HH:mm";
}
return new DateTime(date).toString(DateTimeFormat.forPattern(format));
}
public static String timeString(long mills) {
return new DateTime(mills).toString(DateTimeFormat.shortTime());
String format = "hh:mma";
if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) {
format = "HH:mm";
}
return new DateTime(mills).toString(DateTimeFormat.forPattern(format));
}
public static String timeFullString(long mills) {

View file

@ -28,6 +28,13 @@
android:src="@drawable/ic_add_black_24dp"
android:backgroundTint="@color/defaulttext"/>
<TextView
android:id="@+id/logView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="Log" />
</RelativeLayout>
</FrameLayout>

View file

@ -33,6 +33,11 @@ public class ActionLoopDisableTest {
Assert.assertEquals(R.string.disableloop, actionLoopDisable.friendlyName());
}
@Test
public void shortDescriptionTest() {
Assert.assertEquals(R.string.disableloop, actionLoopDisable.friendlyName());
}
@Test
public void iconTest() {
Assert.assertEquals(Optional.of(R.drawable.ic_stop_24dp), actionLoopDisable.icon());

View file

@ -33,6 +33,11 @@ public class ActionLoopEnableTest {
Assert.assertEquals(R.string.enableloop, actionLoopEnable.friendlyName());
}
@Test
public void shortDescriptionTest() {
Assert.assertEquals(R.string.enableloop, actionLoopEnable.friendlyName());
}
@Test
public void iconTest() {
Assert.assertEquals(Optional.of(R.drawable.ic_play_circle_outline_24dp), actionLoopEnable.icon());

View file

@ -30,6 +30,11 @@ public class ActionLoopResumeTest {
Assert.assertEquals(R.string.resumeloop, actionLoopResume.friendlyName());
}
@Test
public void shortDescriptionTest() {
Assert.assertEquals(R.string.resumeloop, actionLoopResume.friendlyName());
}
@Test
public void iconTest() {
Assert.assertEquals(Optional.of(R.drawable.ic_replay_24dp), actionLoopResume.icon());

View file

@ -34,6 +34,11 @@ public class ActionLoopSuspendTest {
Assert.assertEquals(R.string.suspendloop, actionLoopSuspend.friendlyName());
}
@Test
public void shortDescriptionTest() {
Assert.assertEquals(R.string.suspendloop, actionLoopSuspend.friendlyName());
}
@Test
public void iconTest() {
Assert.assertEquals(Optional.of(R.drawable.ic_pause_circle_outline_24dp), actionLoopSuspend.icon());

View file

@ -41,6 +41,11 @@ public class ActionStartTempTargetTest {
Assert.assertEquals(R.string.starttemptarget, actionStartTempTarget.friendlyName());
}
@Test
public void shortDescriptionTest() {
Assert.assertEquals(R.string.starttemptarget, actionStartTempTarget.friendlyName());
}
@Test
public void iconTest() {
Assert.assertEquals(Optional.of(R.drawable.icon_cp_cgm_target), actionStartTempTarget.icon());

View file

@ -22,7 +22,12 @@ public class ActionTest extends Action {
}
@Override
void doAction(Callback callback) {
public String shortDescription() {
return null;
}
@Override
public void doAction(Callback callback) {
}
@Override