work on automation plugin
- implement clone() methods for actions and triggers - add AutomationEventTest - improve ui
This commit is contained in:
parent
4d98c44d74
commit
97a7a75c86
|
@ -10,7 +10,7 @@ import java.util.List;
|
|||
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
|
||||
|
||||
public class AutomationEvent {
|
||||
public class AutomationEvent implements Cloneable {
|
||||
|
||||
private Trigger trigger;
|
||||
private List<Action> actions = new ArrayList<>();
|
||||
|
@ -57,17 +57,41 @@ public class AutomationEvent {
|
|||
try {
|
||||
JSONObject d = new JSONObject(data);
|
||||
// title
|
||||
title = d.getString("title");
|
||||
title = d.optString("title", "");
|
||||
// trigger
|
||||
trigger = Trigger.instantiate(d.getString("trigger"));
|
||||
// actions
|
||||
JSONArray array = d.getJSONArray("actions");
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
actions.add(Action.instantiate(array.getJSONObject(i)));
|
||||
actions.add(Action.instantiate(new JSONObject(array.getString(i))));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void apply(AutomationEvent event) {
|
||||
trigger = event.trigger;
|
||||
actions = event.actions;
|
||||
title = event.title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutomationEvent clone() throws CloneNotSupportedException {
|
||||
AutomationEvent e = (AutomationEvent) super.clone();
|
||||
e.title = title;
|
||||
|
||||
// clone actions
|
||||
e.actions = new ArrayList<>();
|
||||
for(Action a : actions) {
|
||||
e.actions.add(a.clone());
|
||||
}
|
||||
|
||||
// clone triggers
|
||||
if (trigger != null) {
|
||||
e.trigger = trigger.clone();
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,14 +51,11 @@ public class AutomationFragment extends SubscriberFragment {
|
|||
unbinder = ButterKnife.bind(this, view);
|
||||
|
||||
final AutomationPlugin plugin = AutomationPlugin.getPlugin();
|
||||
mEventListAdapter = new EventListAdapter(plugin.getAutomationEvents());
|
||||
mEventListAdapter = new EventListAdapter(plugin.getAutomationEvents(), getFragmentManager());
|
||||
mEventListView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mEventListView.setAdapter(mEventListAdapter);
|
||||
|
||||
EditEventDialog.setOnClickListener(event -> {
|
||||
plugin.getAutomationEvents().add(event);
|
||||
mEventListAdapter.notifyDataSetChanged();
|
||||
});
|
||||
EditEventDialog.setOnClickListener(event -> mEventListAdapter.notifyDataSetChanged());
|
||||
|
||||
updateGUI();
|
||||
|
||||
|
@ -74,7 +71,7 @@ public class AutomationFragment extends SubscriberFragment {
|
|||
|
||||
@OnClick(R.id.fabAddEvent)
|
||||
void onClickAddEvent(View v) {
|
||||
EditEventDialog dialog = EditEventDialog.newInstance(new AutomationEvent());
|
||||
EditEventDialog dialog = EditEventDialog.newInstance(new AutomationEvent(), true);
|
||||
dialog.show(getFragmentManager(), "EditEventDialog");
|
||||
}
|
||||
|
||||
|
@ -83,9 +80,11 @@ public class AutomationFragment extends SubscriberFragment {
|
|||
*/
|
||||
public static class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
|
||||
private final List<AutomationEvent> mEventList;
|
||||
private final FragmentManager mFragmentManager;
|
||||
|
||||
public EventListAdapter(List<AutomationEvent> events) {
|
||||
public EventListAdapter(List<AutomationEvent> events, FragmentManager fragmentManager) {
|
||||
this.mEventList = events;
|
||||
this.mFragmentManager = fragmentManager;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -132,8 +131,17 @@ public class AutomationFragment extends SubscriberFragment {
|
|||
addImage(res, holder.context, holder.iconLayout);
|
||||
}
|
||||
|
||||
// TODO: check null
|
||||
//holder.eventDescription.setText(event.getTrigger().friendlyDescription());
|
||||
// action: remove
|
||||
holder.iconTrash.setOnClickListener(v -> {
|
||||
mEventList.remove(event);
|
||||
notifyDataSetChanged();
|
||||
});
|
||||
|
||||
// action: edit
|
||||
holder.rootLayout.setOnClickListener(v -> {
|
||||
EditEventDialog dialog = EditEventDialog.newInstance(event, false);
|
||||
dialog.show(mFragmentManager, "EditEventDialog");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -146,6 +154,7 @@ public class AutomationFragment extends SubscriberFragment {
|
|||
final LinearLayout iconLayout;
|
||||
final TextView eventTitle;
|
||||
final Context context;
|
||||
final ImageView iconTrash;
|
||||
|
||||
public ViewHolder(View view, Context context) {
|
||||
super(view);
|
||||
|
@ -153,6 +162,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +234,17 @@ public class AutomationFragment extends SubscriberFragment {
|
|||
build();
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public LinearLayout getRootLayout() {
|
||||
return mRootLayout;
|
||||
}
|
||||
|
||||
public FragmentManager getFragmentManager() {
|
||||
return mFragmentManager;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
mRootLayout.removeAllViews();
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.json.JSONObject;
|
|||
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
|
||||
public abstract class Action {
|
||||
public abstract class Action implements Cloneable {
|
||||
|
||||
public abstract int friendlyName();
|
||||
|
||||
|
@ -33,6 +33,11 @@ public abstract class Action {
|
|||
|
||||
public void copy(Action action) { }
|
||||
|
||||
@Override
|
||||
public Action clone() throws CloneNotSupportedException {
|
||||
return (Action) super.clone();
|
||||
}
|
||||
|
||||
/*package*/ Action fromJSON(String data) {
|
||||
return this;
|
||||
}
|
||||
|
@ -40,9 +45,9 @@ public abstract class Action {
|
|||
public static Action instantiate(JSONObject object) {
|
||||
try {
|
||||
String type = object.getString("type");
|
||||
JSONObject data = object.getJSONObject("data");
|
||||
JSONObject data = object.optJSONObject("data");
|
||||
Class clazz = Class.forName(type);
|
||||
return ((Action) clazz.newInstance()).fromJSON(data.toString());
|
||||
return ((Action) clazz.newInstance()).fromJSON(data != null ? data.toString() : "");
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import info.nightscout.androidaps.utils.JsonHelper;
|
|||
|
||||
public class ActionStartTempTarget extends Action {
|
||||
private String reason;
|
||||
|
||||
private InputBg value;
|
||||
private InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
|
||||
|
||||
|
@ -103,6 +102,15 @@ public class ActionStartTempTarget extends Action {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionStartTempTarget clone() throws CloneNotSupportedException {
|
||||
ActionStartTempTarget a = (ActionStartTempTarget) super.clone();
|
||||
a.reason = reason;
|
||||
a.value = value;
|
||||
a.duration = duration;
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.of(R.drawable.icon_cp_cgm_target);
|
||||
|
|
|
@ -36,7 +36,13 @@ public class EditActionDialog extends DialogFragment {
|
|||
Bundle args = new Bundle();
|
||||
EditActionDialog fragment = new EditActionDialog();
|
||||
fragment.setArguments(args);
|
||||
resultAction = action;
|
||||
|
||||
// clone action to static object
|
||||
try {
|
||||
resultAction = action.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public class EditEventDialog extends DialogFragment {
|
|||
}
|
||||
|
||||
private static OnClickListener mClickListener = null;
|
||||
private static AutomationEvent mEvent;
|
||||
private static AutomationEvent staticEvent;
|
||||
|
||||
public static void setOnClickListener(OnClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
|
@ -51,14 +51,22 @@ public class EditEventDialog extends DialogFragment {
|
|||
|
||||
private Unbinder mUnbinder;
|
||||
private AutomationFragment.ActionListAdapter mActionListAdapter;
|
||||
private AutomationEvent mEvent;
|
||||
private boolean mAddNew;
|
||||
|
||||
public static EditEventDialog newInstance(AutomationEvent event) {
|
||||
mEvent = event; // FIXME
|
||||
public static EditEventDialog newInstance(AutomationEvent event, boolean addNew) {
|
||||
staticEvent = event;
|
||||
|
||||
Bundle args = new Bundle();
|
||||
EditEventDialog fragment = new EditEventDialog();
|
||||
fragment.setArguments(args);
|
||||
|
||||
// clone event
|
||||
try {
|
||||
fragment.mEvent = event.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
fragment.mAddNew = addNew;
|
||||
return fragment;
|
||||
}
|
||||
|
||||
|
@ -70,11 +78,15 @@ public class EditEventDialog extends DialogFragment {
|
|||
// load data from bundle
|
||||
if (savedInstanceState != null) {
|
||||
String eventData = savedInstanceState.getString("event");
|
||||
if (eventData != null) mEvent.fromJSON(eventData);
|
||||
} else {
|
||||
if (eventData != null) mEvent = new AutomationEvent().fromJSON(eventData);
|
||||
mAddNew = savedInstanceState.getBoolean("addNew");
|
||||
} else if (mAddNew) {
|
||||
mEvent.setTrigger(new TriggerConnector(TriggerConnector.Type.OR));
|
||||
}
|
||||
|
||||
// event title
|
||||
mEditEventTitle.setText(mEvent.getTitle());
|
||||
|
||||
// display root trigger
|
||||
mTriggerDescription.setText(mEvent.getTrigger().friendlyDescription());
|
||||
|
||||
|
@ -136,6 +148,15 @@ public class EditEventDialog extends DialogFragment {
|
|||
return;
|
||||
}
|
||||
|
||||
// apply changes
|
||||
staticEvent.apply(mEvent);
|
||||
|
||||
// add new
|
||||
if (mAddNew) {
|
||||
final AutomationPlugin plugin = AutomationPlugin.getPlugin();
|
||||
plugin.getAutomationEvents().add(mEvent);
|
||||
}
|
||||
|
||||
if (mClickListener != null) mClickListener.onClick(mEvent);
|
||||
dismiss();
|
||||
}
|
||||
|
@ -148,6 +169,7 @@ public class EditEventDialog extends DialogFragment {
|
|||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
bundle.putString("event", mEvent.toJSON());
|
||||
bundle.putBoolean("addNew", mAddNew);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.List;
|
|||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
|
||||
public abstract class Trigger {
|
||||
public abstract class Trigger implements Cloneable {
|
||||
|
||||
public enum Comparator {
|
||||
IS_LOWER,
|
||||
|
@ -139,4 +139,11 @@ public abstract class Trigger {
|
|||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger clone() throws CloneNotSupportedException {
|
||||
Trigger t = (Trigger) super.clone();
|
||||
t.connector = connector; // parent should already be cloned
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,4 +259,16 @@ public class TriggerConnector extends Trigger {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TriggerConnector clone() throws CloneNotSupportedException {
|
||||
TriggerConnector tc = (TriggerConnector) super.clone();
|
||||
tc.list = new ArrayList<>();
|
||||
for(Trigger t : list) {
|
||||
tc.list.add(t.clone());
|
||||
}
|
||||
if (adapter != null)
|
||||
tc.adapter = new AutomationFragment.TriggerListAdapter(adapter.getContext(), adapter.getFragmentManager(), adapter.getRootLayout(), tc);
|
||||
return tc;
|
||||
}
|
||||
}
|
||||
|
|
7
app/src/main/res/drawable/ic_trash_outline.xml
Normal file
7
app/src/main/res/drawable/ic_trash_outline.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="@color/defaulttext" android:pathData="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z" />
|
||||
</vector>
|
|
@ -12,21 +12,36 @@
|
|||
android:padding="8dp"
|
||||
android:background="@color/ribbonDefault">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/iconLayout"
|
||||
<ImageView
|
||||
android:id="@+id/iconTrash"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentRight="true"/>
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@drawable/ic_trash_outline"
|
||||
android:contentDescription="Remove"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toLeftOf="@id/iconTrash"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/viewEventTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/iconLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toLeftOf="@id/iconLayout"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,41 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopEnable;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnectorTest;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({})
|
||||
public class AutomationEventTest {
|
||||
@Test
|
||||
public void testCloneEvent() throws CloneNotSupportedException {
|
||||
// create test object
|
||||
AutomationEvent event = new AutomationEvent();
|
||||
event.setTitle("Test");
|
||||
event.setTrigger(Trigger.instantiate(TriggerConnectorTest.oneItem));
|
||||
event.addAction(new ActionLoopEnable());
|
||||
|
||||
// clone
|
||||
AutomationEvent clone = event.clone();
|
||||
|
||||
// check title
|
||||
Assert.assertEquals(event.getTitle(), clone.getTitle());
|
||||
|
||||
// check trigger
|
||||
Assert.assertNotNull(clone.getTrigger());
|
||||
Assert.assertFalse(event.getTrigger() == clone.getTrigger()); // not the same object reference
|
||||
Assert.assertEquals(event.getTrigger().getClass(), clone.getTrigger().getClass());
|
||||
// TODO: check trigger details
|
||||
|
||||
// check action
|
||||
Assert.assertEquals(1, clone.getActions().size());
|
||||
Assert.assertFalse(event.getActions() == clone.getActions()); // not the same object reference
|
||||
// TODO: check action details
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue