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
This commit is contained in:
Nico Schmitz 2018-09-25 00:00:09 +02:00
parent b5f30a0f46
commit c60aae8630
19 changed files with 670 additions and 288 deletions

View file

@ -155,6 +155,7 @@ allprojects {
flatDir { flatDir {
dirs 'libs' dirs 'libs'
} }
maven { url 'https://jitpack.io' }
} }
} }
@ -216,6 +217,8 @@ dependencies {
implementation "com.jakewharton:butterknife:${butterknifeVersion}" implementation "com.jakewharton:butterknife:${butterknifeVersion}"
annotationProcessor "com.jakewharton:butterknife-compiler:${butterknifeVersion}" annotationProcessor "com.jakewharton:butterknife-compiler:${butterknifeVersion}"
implementation 'com.github.DavidProdinger:weekdays-selector:1.0.4'
testImplementation "junit:junit:4.12" testImplementation "junit:junit:4.12"
testImplementation "org.json:json:20140107" testImplementation "org.json:json:20140107"
testImplementation "org.mockito:mockito-core:2.7.22" testImplementation "org.mockito:mockito-core:2.7.22"

View file

@ -12,9 +12,9 @@ public class AutomationEvent {
private List<Action> actions = new ArrayList<>(); private List<Action> actions = new ArrayList<>();
private String title; private String title;
AutomationEvent(String title) { public void setTitle(String title) { this.title = title; }
this.title = title;
} public void setTrigger(Trigger trigger) { this.trigger = trigger; }
public Trigger getTrigger() { public Trigger getTrigger() {
return trigger; return trigger;

View file

@ -3,35 +3,38 @@ package info.nightscout.androidaps.plugins.general.automation;
import android.app.Activity; import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; 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.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import info.nightscout.androidaps.R; import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.Common.SubscriberFragment; import info.nightscout.androidaps.plugins.Common.SubscriberFragment;
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog; import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
public class AutomationFragment extends SubscriberFragment { public class AutomationFragment extends SubscriberFragment {
private RecyclerView mEventListView; @BindView(R.id.eventListView)
private FloatingActionButton mFabAddEvent; RecyclerView mEventListView;
private EventListAdapter mEventListAdapter; private EventListAdapter mEventListAdapter;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.automation_fragment, container, false); View view = inflater.inflate(R.layout.automation_fragment, container, false);
unbinder = ButterKnife.bind(this, view);
mEventListView = view.findViewById(R.id.eventListView);
mFabAddEvent = view.findViewById(R.id.fabAddEvent);
mFabAddEvent.setOnClickListener(this::onClickAddEvent);
final AutomationPlugin plugin = AutomationPlugin.getPlugin(); final AutomationPlugin plugin = AutomationPlugin.getPlugin();
mEventListAdapter = new EventListAdapter(plugin.getAutomationEvents()); mEventListAdapter = new EventListAdapter(plugin.getAutomationEvents());
@ -44,7 +47,7 @@ public class AutomationFragment extends SubscriberFragment {
} }
@Override @Override
protected void updateGUI() { public void updateGUI() {
Activity activity = getActivity(); Activity activity = getActivity();
if (activity != null) if (activity != null)
activity.runOnUiThread(() -> { activity.runOnUiThread(() -> {
@ -52,21 +55,21 @@ public class AutomationFragment extends SubscriberFragment {
}); });
} }
private void onClickAddEvent(View v) { @OnClick(R.id.fabAddEvent)
/*EditEventDialog dialog = EditEventDialog.newInstance(); void onClickAddEvent(View v) {
EditEventDialog dialog = EditEventDialog.newInstance(new AutomationEvent());
FragmentManager manager = getFragmentManager(); FragmentManager manager = getFragmentManager();
dialog.show(manager, "EditEventDialog");*/ dialog.show(manager, "EditEventDialog");
final AutomationPlugin plugin = AutomationPlugin.getPlugin();
plugin.getAutomationEvents().add(new AutomationEvent("Test"));
updateGUI();
} }
/**
* 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> mEvents; private final List<AutomationEvent> mEventList;
EventListAdapter(List<AutomationEvent> events) { EventListAdapter(List<AutomationEvent> events) {
this.mEvents = events; this.mEventList = events;
} }
@NonNull @NonNull
@ -78,23 +81,71 @@ public class AutomationFragment extends SubscriberFragment {
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 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()); holder.eventTitle.setText(event.getTitle());
// TODO: check null
holder.eventDescription.setText(event.getTrigger().friendlyDescription());
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return mEvents.size(); return mEventList.size();
} }
static class ViewHolder extends RecyclerView.ViewHolder { static class ViewHolder extends RecyclerView.ViewHolder {
TextView eventTitle; TextView eventTitle;
TextView eventDescription;
public ViewHolder(View itemView) { public ViewHolder(View view) {
super(itemView); super(view);
eventTitle = itemView.findViewById(R.id.viewEventTitle); 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<Trigger> mTriggerList;
public TriggerListAdapter(LayoutInflater inflater, LinearLayout rootLayout, List<Trigger> 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();
}
}
} }

View file

@ -1,16 +1,92 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs; package info.nightscout.androidaps.plugins.general.automation.dialogs;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.TextInputEditText;
import android.support.v4.app.DialogFragment; 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 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(); Bundle args = new Bundle();
EditEventDialog fragment = new EditEventDialog(); EditEventDialog fragment = new EditEventDialog();
fragment.setArguments(args); fragment.setArguments(args);
return fragment; 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();
}
} }

View file

@ -1,8 +1,15 @@
package info.nightscout.androidaps.plugins.general.automation.triggers; 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.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import info.nightscout.androidaps.R; import info.nightscout.androidaps.R;
public abstract class Trigger { public abstract class Trigger {
@ -15,7 +22,7 @@ public abstract class Trigger {
IS_GREATER, IS_GREATER,
IS_NOT_AVAILABLE; IS_NOT_AVAILABLE;
public int getStringRes() { public @StringRes int getStringRes() {
switch (this) { switch (this) {
case IS_LOWER: case IS_LOWER:
return R.string.islower; return R.string.islower;
@ -56,18 +63,26 @@ public abstract class Trigger {
} }
} }
protected ViewHolder viewHolder = null;
protected TriggerConnector connector = null;
Trigger() { Trigger() {
} }
abstract boolean shouldRun(); public Trigger getConnector() {
return connector;
}
public abstract boolean shouldRun();
abstract String toJSON(); abstract String toJSON();
abstract Trigger fromJSON(String data); abstract Trigger fromJSON(String data);
abstract int friendlyName(); public abstract int friendlyName();
abstract String friendlyDescription(); public abstract String friendlyDescription();
void notifyAboutRun(long time) { void notifyAboutRun(long time) {
} }
@ -82,6 +97,33 @@ public abstract class Trigger {
e.printStackTrace(); e.printStackTrace();
} }
return null; 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; }
} }
} }

View file

@ -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<Trigger> 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);
}
}

View file

@ -1,5 +1,7 @@
package info.nightscout.androidaps.plugins.general.automation.triggers; package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.view.LayoutInflater;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -17,7 +19,7 @@ public class TriggerBg extends Trigger {
protected String units = ProfileFunctions.getInstance().getProfileUnits(); protected String units = ProfileFunctions.getInstance().getProfileUnits();
@Override @Override
synchronized boolean shouldRun() { public synchronized boolean shouldRun() {
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
if (glucoseStatus == null && comparator.equals(Comparator.IS_NOT_AVAILABLE)) if (glucoseStatus == null && comparator.equals(Comparator.IS_NOT_AVAILABLE))
@ -58,18 +60,23 @@ public class TriggerBg extends Trigger {
} }
@Override @Override
int friendlyName() { public int friendlyName() {
return R.string.glucose; return R.string.glucose;
} }
@Override @Override
String friendlyDescription() { public String friendlyDescription() {
if (comparator.equals(Comparator.IS_NOT_AVAILABLE)) if (comparator.equals(Comparator.IS_NOT_AVAILABLE))
return MainApp.gs(R.string.glucoseisnotavailable); return MainApp.gs(R.string.glucoseisnotavailable);
else else
return MainApp.gs(R.string.glucosecompared, comparator.getStringRes(), threshold, units); return MainApp.gs(R.string.glucosecompared, comparator.getStringRes(), threshold, units);
} }
@Override
public ViewHolder createViewHolder(LayoutInflater inflater) {
return null;
}
TriggerBg threshold(double threshold) { TriggerBg threshold(double threshold) {
this.threshold = threshold; this.threshold = threshold;
return this; return this;

View file

@ -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<Trigger> 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();
}
}
}

View file

@ -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<Trigger> 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);
}
}

View file

@ -1,10 +1,18 @@
package info.nightscout.androidaps.plugins.general.automation.triggers; 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.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.List;
import butterknife.BindView;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R; import info.nightscout.androidaps.R;
import info.nightscout.utils.DateUtil; import info.nightscout.utils.DateUtil;
@ -13,6 +21,68 @@ import info.nightscout.utils.T;
public class TriggerTime extends Trigger { 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; long lastRun;
// Single execution // Single execution
@ -20,20 +90,28 @@ public class TriggerTime extends Trigger {
// Recurring // Recurring
boolean 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 hour;
int minute; int minute;
long validTo; 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 @Override
boolean shouldRun() { public boolean shouldRun() {
if (recurring) { if (recurring) {
if (validTo != 0 && DateUtil.now() > validTo) if (validTo != 0 && DateUtil.now() > validTo)
return false; return false;
@ -46,13 +124,7 @@ public class TriggerTime extends Trigger {
scheduledCal.set(Calendar.SECOND, 0); scheduledCal.set(Calendar.SECOND, 0);
long scheduled = scheduledCal.getTimeInMillis(); long scheduled = scheduledCal.getTimeInMillis();
if (monday && scheduledDayOfWeek == Calendar.MONDAY || if (isSet(DayOfWeek.fromCalendarInt(scheduledDayOfWeek))) {
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 (DateUtil.now() >= scheduled && DateUtil.now() - scheduled < T.mins(5).msecs()) { if (DateUtil.now() >= scheduled && DateUtil.now() - scheduled < T.mins(5).msecs()) {
if (lastRun < scheduled) if (lastRun < scheduled)
return true; return true;
@ -75,13 +147,9 @@ public class TriggerTime extends Trigger {
data.put("lastRun", lastRun); data.put("lastRun", lastRun);
data.put("runAt", runAt); data.put("runAt", runAt);
data.put("recurring", recurring); data.put("recurring", recurring);
data.put("monday", monday); for(int i = 0; i < weekdays.length; ++i) {
data.put("tuesday", tuesday); data.put(DayOfWeek.values()[i].name(), weekdays[i]);
data.put("wednesday", wednesday); }
data.put("thursday", thursday);
data.put("friday", friday);
data.put("saturday", saturday);
data.put("sunday", sunday);
data.put("hour", hour); data.put("hour", hour);
data.put("minute", minute); data.put("minute", minute);
data.put("validTo", validTo); data.put("validTo", validTo);
@ -101,13 +169,9 @@ public class TriggerTime extends Trigger {
lastRun = JsonHelper.safeGetLong(o, "lastRun"); lastRun = JsonHelper.safeGetLong(o, "lastRun");
runAt = JsonHelper.safeGetLong(o, "runAt"); runAt = JsonHelper.safeGetLong(o, "runAt");
recurring = JsonHelper.safeGetBoolean(o, "recurring"); recurring = JsonHelper.safeGetBoolean(o, "recurring");
monday = JsonHelper.safeGetBoolean(o, "monday"); for(int i = 0; i < weekdays.length; ++i) {
tuesday = JsonHelper.safeGetBoolean(o, "tuesday"); weekdays[i] = JsonHelper.safeGetBoolean(o, DayOfWeek.values()[i].name());
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");
hour = JsonHelper.safeGetInt(o, "hour"); hour = JsonHelper.safeGetInt(o, "hour");
minute = JsonHelper.safeGetInt(o, "minute"); minute = JsonHelper.safeGetInt(o, "minute");
validTo = JsonHelper.safeGetLong(o, "validTo"); validTo = JsonHelper.safeGetLong(o, "validTo");
@ -118,12 +182,12 @@ public class TriggerTime extends Trigger {
} }
@Override @Override
int friendlyName() { public int friendlyName() {
return R.string.time; return R.string.time;
} }
@Override @Override
String friendlyDescription() { public String friendlyDescription() {
if (recurring) { if (recurring) {
// TODO // TODO
return "Every "; return "Every ";
@ -152,41 +216,6 @@ public class TriggerTime extends Trigger {
return this; 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) { TriggerTime validTo(long validTo) {
this.validTo = validTo; this.validTo = validTo;
return this; return this;
@ -202,4 +231,34 @@ public class TriggerTime extends Trigger {
return this; 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<Integer> 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));
});
}
}
} }

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="5dip"
android:color="@android:color/white" />
</shape>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:orientation="vertical"
android:padding="10dp">
<android.support.design.widget.TextInputEditText
android:id="@+id/inputEventTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Event Name" />
<LinearLayout
android:id="@+id/layoutTrigger"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
<include layout="@layout/mdtp_done_button" />
</LinearLayout>
</FrameLayout>

View file

@ -1,20 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp">
<TextView <TextView
android:id="@+id/viewEventTitle" android:id="@+id/viewEventTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
<TextView
android:id="@+id/viewEventDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

View file

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent" android:layout_width="fill_parent"

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp"
android:background="@drawable/border_automation_unit">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/triggerListLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/buttonAddAnd"
android:text="@string/and"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/buttonAddOr"
android:text="@string/or"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/buttonRemove"
android:text="-"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<com.dpro.widgets.WeekdaysPicker
android:id="@+id/weekdays"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:sunday_first_day="false" />
</LinearLayout>

View file

@ -1230,6 +1230,20 @@
<string name="use_passive_location">Use passive location</string> <string name="use_passive_location">Use passive location</string>
<string name="locationservice">Location service</string> <string name="locationservice">Location service</string>
<string name="key_location" translatable="false">location</string> <string name="key_location" translatable="false">location</string>
<string name="weekday_monday">Monday</string>
<string name="weekday_tuesday">Tuesday</string>
<string name="weekday_wednesday">Wednesday</string>
<string name="weekday_thursday">Thursday</string>
<string name="weekday_friday">Friday</string>
<string name="weekday_saturday">Saturday</string>
<string name="weekday_sunday">Sunday</string>
<string name="weekday_monday_short">Mon</string>
<string name="weekday_tuesday_short">Tue</string>
<string name="weekday_wednesday_short">Wed</string>
<string name="weekday_thursday_short">Thu</string>
<string name="weekday_friday_short">Fri</string>
<string name="weekday_saturday_short">Sat</string>
<string name="weekday_sunday_short">Sun</string>
<plurals name="objective_days"> <plurals name="objective_days">
<item quantity="one">%1$d day</item> <item quantity="one">%1$d day</item>

View file

@ -8,9 +8,6 @@ import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerAnd;
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({}) @PrepareForTest({})
public class TriggerAndTest { public class TriggerAndTest {

View file

@ -8,9 +8,6 @@ import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerOr;
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({}) @PrepareForTest({})
public class TriggerOrTest { public class TriggerOrTest {