From c60aae863019a23283fe62ca64d798fa44c45628 Mon Sep 17 00:00:00 2001 From: Nico Schmitz Date: Tue, 25 Sep 2018 00:00:09 +0200 Subject: [PATCH] Work on implementation for automation plugin. - Use butterknife - Import weekdays selector - Implement layout for trigger connection and trigger time - Implement view holders - Implement adapters - Implement dummy code for quick testings - Refactor code --- app/build.gradle | 3 + .../general/automation/AutomationEvent.java | 6 +- .../automation/AutomationFragment.java | 97 +++++++-- .../automation/dialogs/EditEventDialog.java | 80 ++++++- .../general/automation/triggers/Trigger.java | 50 ++++- .../automation/triggers/TriggerAnd.java | 87 -------- .../automation/triggers/TriggerBg.java | 13 +- .../automation/triggers/TriggerConnector.java | 205 ++++++++++++++++++ .../automation/triggers/TriggerOr.java | 87 -------- .../automation/triggers/TriggerTime.java | 191 ++++++++++------ .../res/drawable/border_automation_unit.xml | 7 + .../res/layout/automation_dialog_event.xml | 31 +++ .../main/res/layout/automation_event_item.xml | 20 +- .../main/res/layout/automation_fragment.xml | 1 + .../layout/automation_trigger_connector.xml | 46 ++++ .../res/layout/automation_trigger_time.xml | 14 ++ app/src/main/res/values/strings.xml | 14 ++ .../automation/triggers/TriggerAndTest.java | 3 - .../automation/triggers/TriggerOrTest.java | 3 - 19 files changed, 670 insertions(+), 288 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAnd.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerOr.java create mode 100644 app/src/main/res/drawable/border_automation_unit.xml create mode 100644 app/src/main/res/layout/automation_dialog_event.xml create mode 100644 app/src/main/res/layout/automation_trigger_connector.xml create mode 100644 app/src/main/res/layout/automation_trigger_time.xml diff --git a/app/build.gradle b/app/build.gradle index 1f86c015dd..f56f727cd4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -155,6 +155,7 @@ allprojects { flatDir { dirs 'libs' } + maven { url 'https://jitpack.io' } } } @@ -216,6 +217,8 @@ dependencies { implementation "com.jakewharton:butterknife:${butterknifeVersion}" annotationProcessor "com.jakewharton:butterknife-compiler:${butterknifeVersion}" + implementation 'com.github.DavidProdinger:weekdays-selector:1.0.4' + testImplementation "junit:junit:4.12" testImplementation "org.json:json:20140107" testImplementation "org.mockito:mockito-core:2.7.22" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java index 8e71c19d6e..f7cd5c6510 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java @@ -12,9 +12,9 @@ public class AutomationEvent { private List actions = new ArrayList<>(); private String title; - AutomationEvent(String title) { - this.title = title; - } + public void setTitle(String title) { this.title = title; } + + public void setTrigger(Trigger trigger) { this.trigger = trigger; } public Trigger getTrigger() { return trigger; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.java index 7dce538747..8efc953bc7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.java @@ -3,35 +3,38 @@ package info.nightscout.androidaps.plugins.general.automation; import android.app.Activity; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.design.widget.FloatingActionButton; +import android.support.v4.app.FragmentManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.LinearLayout; import android.widget.TextView; +import java.util.ArrayList; import java.util.List; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.Common.SubscriberFragment; import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog; +import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger; public class AutomationFragment extends SubscriberFragment { - private RecyclerView mEventListView; - private FloatingActionButton mFabAddEvent; + @BindView(R.id.eventListView) + RecyclerView mEventListView; + private EventListAdapter mEventListAdapter; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.automation_fragment, container, false); - - mEventListView = view.findViewById(R.id.eventListView); - mFabAddEvent = view.findViewById(R.id.fabAddEvent); - - mFabAddEvent.setOnClickListener(this::onClickAddEvent); + unbinder = ButterKnife.bind(this, view); final AutomationPlugin plugin = AutomationPlugin.getPlugin(); mEventListAdapter = new EventListAdapter(plugin.getAutomationEvents()); @@ -44,7 +47,7 @@ public class AutomationFragment extends SubscriberFragment { } @Override - protected void updateGUI() { + public void updateGUI() { Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(() -> { @@ -52,21 +55,21 @@ public class AutomationFragment extends SubscriberFragment { }); } - private void onClickAddEvent(View v) { - /*EditEventDialog dialog = EditEventDialog.newInstance(); + @OnClick(R.id.fabAddEvent) + void onClickAddEvent(View v) { + EditEventDialog dialog = EditEventDialog.newInstance(new AutomationEvent()); FragmentManager manager = getFragmentManager(); - dialog.show(manager, "EditEventDialog");*/ - - final AutomationPlugin plugin = AutomationPlugin.getPlugin(); - plugin.getAutomationEvents().add(new AutomationEvent("Test")); - updateGUI(); + dialog.show(manager, "EditEventDialog"); } + /** + * RecyclerViewAdapter to display event lists. + */ public static class EventListAdapter extends RecyclerView.Adapter { - private final List mEvents; + private final List mEventList; EventListAdapter(List events) { - this.mEvents = events; + this.mEventList = events; } @NonNull @@ -78,23 +81,71 @@ public class AutomationFragment extends SubscriberFragment { @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - final AutomationEvent event = mEvents.get(position); + final AutomationEvent event = mEventList.get(position); holder.eventTitle.setText(event.getTitle()); + + // TODO: check null + holder.eventDescription.setText(event.getTrigger().friendlyDescription()); } @Override public int getItemCount() { - return mEvents.size(); + return mEventList.size(); } static class ViewHolder extends RecyclerView.ViewHolder { TextView eventTitle; + TextView eventDescription; - public ViewHolder(View itemView) { - super(itemView); - eventTitle = itemView.findViewById(R.id.viewEventTitle); + public ViewHolder(View view) { + super(view); + eventTitle = view.findViewById(R.id.viewEventTitle); + eventDescription = view.findViewById(R.id.viewEventDescription); } } } + /** + * Custom Adapter to display triggers dynamically with nested linear layouts. + */ + public static class TriggerListAdapter { + private final LinearLayout mRootLayout; + private final LayoutInflater mInflater; + private final List mTriggerList; + + public TriggerListAdapter(LayoutInflater inflater, LinearLayout rootLayout, List triggers) { + mRootLayout = rootLayout; + mInflater = inflater; + mTriggerList = triggers; + build(); + } + + public TriggerListAdapter(LayoutInflater inflater, LinearLayout rootLayout, Trigger trigger) { + mRootLayout = rootLayout; + mInflater = inflater; + mTriggerList = new ArrayList<>(); + mTriggerList.add(trigger); + build(); + } + + public void destroy() { + mRootLayout.removeAllViews(); + for(Trigger trigger : mTriggerList) { + trigger.destroyViewHolder(); + } + } + + private void build() { + for(Trigger trigger : mTriggerList) { + Trigger.ViewHolder viewHolder = trigger.createViewHolder(mInflater); + mRootLayout.addView(viewHolder.getView()); + } + } + + public void rebuild() { + destroy(); + build(); + } + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.java index c7ab2dbf7b..fa9150033a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.java @@ -1,16 +1,92 @@ package info.nightscout.androidaps.plugins.general.automation.dialogs; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.TextInputEditText; import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import butterknife.Unbinder; +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.triggers.TriggerConnector; +import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTime; public class EditEventDialog extends DialogFragment { - public static EditEventDialog newInstance() { + + private static AutomationEvent mEvent; + + @BindView(R.id.inputEventTitle) + TextInputEditText mEditEventTitle; + + @BindView(R.id.layoutTrigger) + LinearLayout mLayoutTrigger; + + private Unbinder mUnbinder; + private AutomationFragment.TriggerListAdapter mTriggerListAdapter; + + public static EditEventDialog newInstance(AutomationEvent event) { + mEvent = event; Bundle args = new Bundle(); - EditEventDialog fragment = new EditEventDialog(); fragment.setArguments(args); return fragment; } + + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.automation_dialog_event, container, false); + mUnbinder = ButterKnife.bind(this, view); + + // dummy initialization + TriggerConnector to = new TriggerConnector(TriggerConnector.Type.OR); + to.add(new TriggerTime()); + to.add(new TriggerTime()); + TriggerConnector ta = new TriggerConnector(); + ta.add(to); + mEvent.setTrigger(ta); + + // display triggers + mTriggerListAdapter = new AutomationFragment.TriggerListAdapter(getLayoutInflater(), mLayoutTrigger, mEvent.getTrigger()); + + return view; + } + + @Override + public void onDestroyView() { + mTriggerListAdapter.destroy(); + mUnbinder.unbind(); + super.onDestroyView(); + } + + @OnClick(R.id.ok) + public void onButtonOk(View view) { + String title = mEditEventTitle.getText().toString(); + if (title.isEmpty()) return; + + mEvent.setTitle(title); + + + final AutomationPlugin plugin = AutomationPlugin.getPlugin(); + plugin.getAutomationEvents().add(mEvent); + + dismiss(); + } + + @OnClick(R.id.cancel) + public void onButtonCancel(View view) { + dismiss(); + } + + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java index 20bf93de9c..582080ee58 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/Trigger.java @@ -1,8 +1,15 @@ package info.nightscout.androidaps.plugins.general.automation.triggers; +import android.support.annotation.LayoutRes; +import android.support.annotation.StringRes; +import android.view.LayoutInflater; +import android.view.View; + import org.json.JSONException; import org.json.JSONObject; +import butterknife.ButterKnife; +import butterknife.Unbinder; import info.nightscout.androidaps.R; public abstract class Trigger { @@ -15,7 +22,7 @@ public abstract class Trigger { IS_GREATER, IS_NOT_AVAILABLE; - public int getStringRes() { + public @StringRes int getStringRes() { switch (this) { case IS_LOWER: return R.string.islower; @@ -56,18 +63,26 @@ public abstract class Trigger { } } + protected ViewHolder viewHolder = null; + + protected TriggerConnector connector = null; + Trigger() { } - abstract boolean shouldRun(); + public Trigger getConnector() { + return connector; + } + + public abstract boolean shouldRun(); abstract String toJSON(); abstract Trigger fromJSON(String data); - abstract int friendlyName(); + public abstract int friendlyName(); - abstract String friendlyDescription(); + public abstract String friendlyDescription(); void notifyAboutRun(long time) { } @@ -82,6 +97,33 @@ public abstract class Trigger { e.printStackTrace(); } return null; + } + public abstract ViewHolder createViewHolder(LayoutInflater inflater); + + public ViewHolder getViewHolder() { + return viewHolder; + } + + public void destroyViewHolder() { + if (viewHolder != null) { + viewHolder.destroy(); + } + } + + public static abstract class ViewHolder { + final View view; + final Unbinder unbinder; + + public ViewHolder(LayoutInflater inflater, @LayoutRes int layout) { + view = inflater.inflate(layout, null); + unbinder = ButterKnife.bind(this, view); + } + + public void destroy() { + unbinder.unbind(); + } + + public View getView() { return view; } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAnd.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAnd.java deleted file mode 100644 index 3e791afcc7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerAnd.java +++ /dev/null @@ -1,87 +0,0 @@ -package info.nightscout.androidaps.plugins.general.automation.triggers; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.R; - -public class TriggerAnd extends Trigger { - - private List list = new ArrayList<>(); - - @Override - synchronized boolean shouldRun() { - boolean result = true; - - for (Trigger t : list) { - result = result && t.shouldRun(); - } - return result; - } - - @Override - synchronized String toJSON() { - JSONObject o = new JSONObject(); - try { - o.put("type", TriggerAnd.class.getName()); - JSONArray array = new JSONArray(); - for (Trigger t : list) { - array.put(t.toJSON()); - } - o.put("data", array.toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - return o.toString(); - } - - @Override - Trigger fromJSON(String data) { - try { - JSONArray array = new JSONArray(data); - for (int i = 0; i < array.length(); i++) { - Trigger newItem = instantiate(new JSONObject(array.getString(i))); - list.add(newItem); - } - } catch (JSONException e) { - e.printStackTrace(); - } - return this; - } - - @Override - int friendlyName() { - return R.string.and; - } - - @Override - String friendlyDescription() { - int counter = 0; - StringBuilder result = new StringBuilder(); - for (Trigger t : list) { - if (counter++ > 0) result.append(R.string.and); - result.append(t.friendlyDescription()); - } - return result.toString(); - } - - synchronized void add(Trigger t) { - list.add(t); - } - - synchronized boolean remove(Trigger t) { - return list.remove(t); - } - - int size() { - return list.size(); - } - - Trigger get(int i) { - return list.get(i); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java index 7024eecd3f..2a91f2ddd6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBg.java @@ -1,5 +1,7 @@ package info.nightscout.androidaps.plugins.general.automation.triggers; +import android.view.LayoutInflater; + import org.json.JSONException; import org.json.JSONObject; @@ -17,7 +19,7 @@ public class TriggerBg extends Trigger { protected String units = ProfileFunctions.getInstance().getProfileUnits(); @Override - synchronized boolean shouldRun() { + public synchronized boolean shouldRun() { GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); if (glucoseStatus == null && comparator.equals(Comparator.IS_NOT_AVAILABLE)) @@ -58,18 +60,23 @@ public class TriggerBg extends Trigger { } @Override - int friendlyName() { + public int friendlyName() { return R.string.glucose; } @Override - String friendlyDescription() { + public String friendlyDescription() { if (comparator.equals(Comparator.IS_NOT_AVAILABLE)) return MainApp.gs(R.string.glucoseisnotavailable); else return MainApp.gs(R.string.glucosecompared, comparator.getStringRes(), threshold, units); } + @Override + public ViewHolder createViewHolder(LayoutInflater inflater) { + return null; + } + TriggerBg threshold(double threshold) { this.threshold = threshold; return this; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java new file mode 100644 index 0000000000..0e3528dc02 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerConnector.java @@ -0,0 +1,205 @@ +package info.nightscout.androidaps.plugins.general.automation.triggers; + +import android.support.annotation.StringRes; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.OnClick; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.general.automation.AutomationFragment; +import info.nightscout.utils.JsonHelper; + +public class TriggerConnector extends Trigger { + public enum Type { + AND, + OR; + + public boolean apply(boolean a, boolean b) { + switch (this) { + case AND: + return a && b; + case OR: + return a || b; + } + return false; + } + + public @StringRes int getStringRes() { + switch (this) { + case OR: + return R.string.or; + + default: + case AND: + return R.string.and; + } + } + } + + protected List list = new ArrayList<>(); + private Type connectorType; + + public TriggerConnector() { + connectorType = Type.AND; + } + + public TriggerConnector(Type connectorType) { + this.connectorType = connectorType; + } + + public void changeConnectorType(Type type) { this.connectorType = type; } + + public Type getConnectorType() { return connectorType; } + + public synchronized void add(Trigger t) { + list.add(t); + t.connector = this; + } + + public synchronized boolean remove(Trigger t) { + return list.remove(t); + } + + public int size() { + return list.size(); + } + + public Trigger get(int i) { + return list.get(i); + } + + @Override + public synchronized boolean shouldRun() { + boolean result = false; + + for (Trigger t : list) { + result = connectorType.apply(result, t.shouldRun()); + } + return result; + } + + @Override + synchronized String toJSON() { + JSONObject o = new JSONObject(); + try { + o.put("type", TriggerConnector.class.getName()); + JSONObject data = new JSONObject(); + data.put("connectorType", connectorType.toString()); + JSONArray array = new JSONArray(); + for (Trigger t : list) { + array.put(t.toJSON()); + } + data.put("triggerList", array); + o.put("data", data); + } catch (JSONException e) { + e.printStackTrace(); + } + return o.toString(); + } + + @Override + Trigger fromJSON(String data) { + try { + JSONObject d = new JSONObject(data); + connectorType = Type.valueOf(JsonHelper.safeGetString(d, "connectorType")); + JSONArray array = d.getJSONArray("triggerList"); + for (int i = 0; i < array.length(); i++) { + Trigger newItem = instantiate(new JSONObject(array.getString(i))); + add(newItem); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return this; + } + + @Override + public int friendlyName() { + return connectorType.getStringRes(); + } + + @Override + public String friendlyDescription() { + int counter = 0; + StringBuilder result = new StringBuilder(); + for (Trigger t : list) { + if (counter++ > 0) result.append(friendlyName()); + result.append(t.friendlyDescription()); + } + return result.toString(); + } + + @Override + public ViewHolder createViewHolder(LayoutInflater inflater) { + ViewHolder v = new ViewHolder(inflater); + viewHolder = v; + return v; + } + + + class ViewHolder extends Trigger.ViewHolder { + + @BindView(R.id.triggerListLayout) + LinearLayout triggerListLayout; + + @BindView(R.id.title) + TextView titleView; + + AutomationFragment.TriggerListAdapter adapter; + + public ViewHolder(LayoutInflater inflater) { + super(inflater, R.layout.automation_trigger_connector); + titleView.setText(friendlyName()); + adapter = new AutomationFragment.TriggerListAdapter(inflater, triggerListLayout, list); + } + + @OnClick(R.id.buttonRemove) + public void onButtonClickRemove(View view) { + if (connector != null) { + connector.remove(TriggerConnector.this); + ((TriggerConnector.ViewHolder)connector.getViewHolder()).adapter.rebuild(); + } else { + // no parent + list.clear(); + adapter.rebuild(); + } + } + + @OnClick(R.id.buttonAddAnd) + public void onButtonClickAnd(View view) { + addTrigger(new TriggerTime(), Type.AND); + } + + @OnClick(R.id.buttonAddOr) + public void onButtonClickOr(View view) { + addTrigger(new TriggerTime(), Type.OR); + } + + private void addTrigger(Trigger trigger, Type connection) { + if (getConnectorType().equals(connection)) { + add(trigger); + } else { + TriggerConnector t = new TriggerConnector(connection); + t.add(trigger); + add(t); + } + adapter.rebuild(); + } + + @Override + public void destroy() { + adapter.destroy(); + super.destroy(); + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerOr.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerOr.java deleted file mode 100644 index 309838b25f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerOr.java +++ /dev/null @@ -1,87 +0,0 @@ -package info.nightscout.androidaps.plugins.general.automation.triggers; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.R; - -public class TriggerOr extends Trigger { - - private List list = new ArrayList<>(); - - @Override - synchronized boolean shouldRun() { - boolean result = false; - - for (Trigger t : list) { - result = result || t.shouldRun(); - } - return result; - } - - @Override - synchronized String toJSON() { - JSONObject o = new JSONObject(); - try { - o.put("type", TriggerOr.class.getName()); - JSONArray array = new JSONArray(); - for (Trigger t : list) { - array.put(t.toJSON()); - } - o.put("data", array.toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - return o.toString(); - } - - @Override - Trigger fromJSON(String data) { - try { - JSONArray array = new JSONArray(data); - for (int i = 0; i < array.length(); i++) { - Trigger newItem = instantiate(new JSONObject(array.getString(i))); - list.add(newItem); - } - } catch (JSONException e) { - e.printStackTrace(); - } - return this; - } - - @Override - int friendlyName() { - return R.string.or; - } - - @Override - String friendlyDescription() { - int counter = 0; - StringBuilder result = new StringBuilder(); - for (Trigger t : list) { - if (counter++ > 0) result.append(R.string.or); - result.append(t.friendlyDescription()); - } - return result.toString(); - } - - synchronized void add(Trigger t) { - list.add(t); - } - - synchronized boolean remove(Trigger t) { - return list.remove(t); - } - - int size() { - return list.size(); - } - - Trigger get(int i) { - return list.get(i); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java index b5f0e6e072..b18236569b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerTime.java @@ -1,10 +1,18 @@ package info.nightscout.androidaps.plugins.general.automation.triggers; +import android.support.annotation.StringRes; +import android.view.LayoutInflater; + +import com.dpro.widgets.WeekdaysPicker; + import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; import java.util.Calendar; +import java.util.List; +import butterknife.BindView; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.utils.DateUtil; @@ -13,6 +21,68 @@ import info.nightscout.utils.T; public class TriggerTime extends Trigger { + public enum DayOfWeek { + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY, + SUNDAY; + + private static final int[] calendarInts = new int[] { + Calendar.MONDAY, + Calendar.TUESDAY, + Calendar.WEDNESDAY, + Calendar.THURSDAY, + Calendar.FRIDAY, + Calendar.SATURDAY, + Calendar.SUNDAY + }; + + private static final int[] fullNames = new int[] { + R.string.weekday_monday, + R.string.weekday_tuesday, + R.string.weekday_wednesday, + R.string.weekday_thursday, + R.string.weekday_friday, + R.string.weekday_saturday, + R.string.weekday_sunday + }; + + private static final int[] shortNames = new int[] { + R.string.weekday_monday_short, + R.string.weekday_tuesday_short, + R.string.weekday_wednesday_short, + R.string.weekday_thursday_short, + R.string.weekday_friday_short, + R.string.weekday_saturday_short, + R.string.weekday_sunday_short + }; + + public int toCalendarInt() { + return calendarInts[ordinal()]; + } + + public static DayOfWeek fromCalendarInt(int day) { + for(int i = 0; i < calendarInts.length; ++i) { + if (calendarInts[i] == day) + return values()[i]; + } + return null; + } + + public @StringRes int getFullName() { + return fullNames[ordinal()]; + } + + public @StringRes int getShortName() { + return shortNames[ordinal()]; + } + } + + private final boolean[] weekdays = new boolean[DayOfWeek.values().length]; + long lastRun; // Single execution @@ -20,20 +90,28 @@ public class TriggerTime extends Trigger { // Recurring boolean recurring; - boolean monday = true; - boolean tuesday = true; - boolean wednesday = true; - boolean thursday = true; - boolean friday = true; - boolean saturday = true; - boolean sunday = true; int hour; int minute; long validTo; + public TriggerTime() { + for(DayOfWeek day : DayOfWeek.values()) { + set(day, true); + } + } + + public TriggerTime set(DayOfWeek day, boolean value) { + weekdays[day.ordinal()] = value; + return this; + } + + public boolean isSet(DayOfWeek day) { + return weekdays[day.ordinal()]; + } + @Override - boolean shouldRun() { + public boolean shouldRun() { if (recurring) { if (validTo != 0 && DateUtil.now() > validTo) return false; @@ -46,13 +124,7 @@ public class TriggerTime extends Trigger { scheduledCal.set(Calendar.SECOND, 0); long scheduled = scheduledCal.getTimeInMillis(); - if (monday && scheduledDayOfWeek == Calendar.MONDAY || - tuesday && scheduledDayOfWeek == Calendar.TUESDAY || - wednesday && scheduledDayOfWeek == Calendar.WEDNESDAY || - thursday && scheduledDayOfWeek == Calendar.THURSDAY || - friday && scheduledDayOfWeek == Calendar.FRIDAY || - saturday && scheduledDayOfWeek == Calendar.SATURDAY || - sunday && scheduledDayOfWeek == Calendar.SUNDAY) { + if (isSet(DayOfWeek.fromCalendarInt(scheduledDayOfWeek))) { if (DateUtil.now() >= scheduled && DateUtil.now() - scheduled < T.mins(5).msecs()) { if (lastRun < scheduled) return true; @@ -75,13 +147,9 @@ public class TriggerTime extends Trigger { data.put("lastRun", lastRun); data.put("runAt", runAt); data.put("recurring", recurring); - data.put("monday", monday); - data.put("tuesday", tuesday); - data.put("wednesday", wednesday); - data.put("thursday", thursday); - data.put("friday", friday); - data.put("saturday", saturday); - data.put("sunday", sunday); + for(int i = 0; i < weekdays.length; ++i) { + data.put(DayOfWeek.values()[i].name(), weekdays[i]); + } data.put("hour", hour); data.put("minute", minute); data.put("validTo", validTo); @@ -101,13 +169,9 @@ public class TriggerTime extends Trigger { lastRun = JsonHelper.safeGetLong(o, "lastRun"); runAt = JsonHelper.safeGetLong(o, "runAt"); recurring = JsonHelper.safeGetBoolean(o, "recurring"); - monday = JsonHelper.safeGetBoolean(o, "monday"); - tuesday = JsonHelper.safeGetBoolean(o, "tuesday"); - wednesday = JsonHelper.safeGetBoolean(o, "wednesday"); - thursday = JsonHelper.safeGetBoolean(o, "thursday"); - friday = JsonHelper.safeGetBoolean(o, "friday"); - saturday = JsonHelper.safeGetBoolean(o, "saturday"); - sunday = JsonHelper.safeGetBoolean(o, "sunday"); + for(int i = 0; i < weekdays.length; ++i) { + weekdays[i] = JsonHelper.safeGetBoolean(o, DayOfWeek.values()[i].name()); + } hour = JsonHelper.safeGetInt(o, "hour"); minute = JsonHelper.safeGetInt(o, "minute"); validTo = JsonHelper.safeGetLong(o, "validTo"); @@ -118,12 +182,12 @@ public class TriggerTime extends Trigger { } @Override - int friendlyName() { + public int friendlyName() { return R.string.time; } @Override - String friendlyDescription() { + public String friendlyDescription() { if (recurring) { // TODO return "Every "; @@ -152,41 +216,6 @@ public class TriggerTime extends Trigger { return this; } - TriggerTime monday(boolean monday) { - this.monday = monday; - return this; - } - - TriggerTime tuesday(boolean tuesday) { - this.tuesday = tuesday; - return this; - } - - TriggerTime wednesday(boolean wednesday) { - this.wednesday = wednesday; - return this; - } - - TriggerTime thursday(boolean thursday) { - this.thursday = thursday; - return this; - } - - TriggerTime friday(boolean friday) { - this.friday = friday; - return this; - } - - TriggerTime saturday(boolean saturday) { - this.saturday = saturday; - return this; - } - - TriggerTime sunday(boolean sunday) { - this.sunday = sunday; - return this; - } - TriggerTime validTo(long validTo) { this.validTo = validTo; return this; @@ -202,4 +231,34 @@ public class TriggerTime extends Trigger { return this; } + @Override + public ViewHolder createViewHolder(LayoutInflater inflater) { + ViewHolder v = new ViewHolder(inflater); + viewHolder = v; + return v; + } + + class ViewHolder extends Trigger.ViewHolder { + + @BindView(R.id.weekdays) + WeekdaysPicker weekdaysPicker; + + public ViewHolder(LayoutInflater inflater) { + super(inflater, R.layout.automation_trigger_time); + + List selectedDays = new ArrayList<>(); + for(int i = 0; i < weekdays.length; ++i) { + DayOfWeek day = DayOfWeek.values()[i]; + boolean selected = weekdays[i]; + if (selected) selectedDays.add(day.toCalendarInt()); + } + weekdaysPicker.setSelectedDays(selectedDays); + + weekdaysPicker.setOnWeekdaysChangeListener((view, i, list) -> { + set(DayOfWeek.fromCalendarInt(i), list.contains(i)); + }); + } + + } + } diff --git a/app/src/main/res/drawable/border_automation_unit.xml b/app/src/main/res/drawable/border_automation_unit.xml new file mode 100644 index 0000000000..6ac70390ae --- /dev/null +++ b/app/src/main/res/drawable/border_automation_unit.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_dialog_event.xml b/app/src/main/res/layout/automation_dialog_event.xml new file mode 100644 index 0000000000..3b49181008 --- /dev/null +++ b/app/src/main/res/layout/automation_dialog_event.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_event_item.xml b/app/src/main/res/layout/automation_event_item.xml index 92c9ba93bc..dc80aeaf15 100644 --- a/app/src/main/res/layout/automation_event_item.xml +++ b/app/src/main/res/layout/automation_event_item.xml @@ -1,20 +1,26 @@ - + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginBottom="8dp" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp"> - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_fragment.xml b/app/src/main/res/layout/automation_fragment.xml index 4b6ee5c71d..a43225cb44 100644 --- a/app/src/main/res/layout/automation_fragment.xml +++ b/app/src/main/res/layout/automation_fragment.xml @@ -1,3 +1,4 @@ + + + + + + + + + +