Update to recent upstream/dev
This commit is contained in:
commit
ba0625b03f
|
@ -156,6 +156,7 @@ allprojects {
|
|||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,6 +219,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"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="sugar.free.sightremote.HISTORY_BROADCASTS" />
|
||||
|
@ -150,7 +151,10 @@
|
|||
android:name=".services.DataService"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".plugins.pump.danaR.services.DanaRExecutionService"
|
||||
android:name=".services.LocationService"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".plugins.PumpDanaR.services.DanaRExecutionService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<service
|
||||
|
|
|
@ -39,6 +39,7 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugi
|
|||
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin;
|
||||
import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment;
|
||||
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils;
|
||||
|
@ -195,6 +196,7 @@ public class MainApp extends Application {
|
|||
pluginsList.add(PersistentNotificationPlugin.getPlugin());
|
||||
pluginsList.add(NSClientPlugin.getPlugin());
|
||||
pluginsList.add(MaintenancePlugin.initPlugin(this));
|
||||
pluginsList.add(AutomationPlugin.getPlugin());
|
||||
|
||||
pluginsList.add(ConfigBuilderPlugin.getPlugin());
|
||||
|
||||
|
@ -424,4 +426,9 @@ public class MainApp extends Application {
|
|||
sDatabaseHelper = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int dpToPx(int dp) {
|
||||
float scale = sResources.getDisplayMetrics().density;
|
||||
return (int) (dp*scale + 0.5f);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlug
|
|||
import info.nightscout.androidaps.utils.LocaleHelper;
|
||||
import info.nightscout.androidaps.utils.OKDialog;
|
||||
import info.nightscout.androidaps.utils.SP;
|
||||
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
|
||||
|
||||
public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
MyPreferenceFragment myPreferenceFragment;
|
||||
|
@ -183,6 +184,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
|
|||
|
||||
addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL);
|
||||
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL);
|
||||
addPreferencesFromResourceIfEnabled(AutomationPlugin.getPlugin(), PluginType.GENERAL);
|
||||
|
||||
addPreferencesFromResource(R.xml.pref_others);
|
||||
addPreferencesFromResource(R.xml.pref_datachoices);
|
||||
|
|
|
@ -45,6 +45,11 @@ public class PumpEnactResult {
|
|||
return this;
|
||||
}
|
||||
|
||||
public PumpEnactResult comment(int comment) {
|
||||
this.comment = MainApp.gs(comment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PumpEnactResult duration(int duration) {
|
||||
this.duration = duration;
|
||||
return this;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package info.nightscout.androidaps.events;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
public class EventLocationChange extends Event {
|
||||
public Location location;
|
||||
|
||||
public EventLocationChange(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
}
|
|
@ -95,6 +95,7 @@ public class L {
|
|||
public static final String PROFILE = "PROFILE";
|
||||
public static final String CONFIGBUILDER = "CONFIGBUILDER";
|
||||
public static final String UI = "UI";
|
||||
public static final String LOCATION = "LOCATION";
|
||||
public static final String SMS = "SMS";
|
||||
|
||||
private static void initialize() {
|
||||
|
@ -110,6 +111,7 @@ public class L {
|
|||
logElements.add(new LogElement(DATASERVICE, true));
|
||||
logElements.add(new LogElement(DATATREATMENTS, true));
|
||||
logElements.add(new LogElement(EVENTS, false, true));
|
||||
logElements.add(new LogElement(LOCATION, true));
|
||||
logElements.add(new LogElement(NOTIFICATION, true));
|
||||
logElements.add(new LogElement(NSCLIENT, true));
|
||||
logElements.add(new LogElement(OVERVIEW, true));
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 implements Cloneable {
|
||||
|
||||
private Trigger trigger;
|
||||
private List<Action> actions = new ArrayList<>();
|
||||
private String title;
|
||||
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
|
||||
public void setTrigger(Trigger trigger) { this.trigger = trigger; }
|
||||
|
||||
public Trigger getTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
|
||||
public List<Action> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
public void addAction(Action action) { actions.add(action); }
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String toJSON() {
|
||||
JSONObject o = new JSONObject();
|
||||
try {
|
||||
// title
|
||||
o.put("title", title);
|
||||
// trigger
|
||||
o.put("trigger", trigger.toJSON());
|
||||
// actions
|
||||
JSONArray array = new JSONArray();
|
||||
for (Action a : actions) {
|
||||
array.put(a.toJSON());
|
||||
}
|
||||
o.put("actions", array);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return o.toString();
|
||||
}
|
||||
|
||||
public AutomationEvent fromJSON(String data) {
|
||||
try {
|
||||
JSONObject d = new JSONObject(data);
|
||||
// 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(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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,394 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
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.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.plugins.common.SubscriberFragment;
|
||||
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.triggers.Trigger;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
|
||||
|
||||
public class AutomationFragment extends SubscriberFragment {
|
||||
|
||||
@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);
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
|
||||
final AutomationPlugin plugin = AutomationPlugin.getPlugin();
|
||||
mEventListAdapter = new EventListAdapter(plugin.getAutomationEvents(), getFragmentManager());
|
||||
mEventListView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mEventListView.setAdapter(mEventListAdapter);
|
||||
|
||||
EditEventDialog.setOnClickListener(event -> mEventListAdapter.notifyDataSetChanged());
|
||||
|
||||
updateGUI();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGUI() {
|
||||
Activity activity = getActivity();
|
||||
if (activity != null)
|
||||
activity.runOnUiThread(() -> mEventListAdapter.notifyDataSetChanged());
|
||||
}
|
||||
|
||||
@OnClick(R.id.fabAddEvent)
|
||||
void onClickAddEvent(View v) {
|
||||
EditEventDialog dialog = EditEventDialog.newInstance(new AutomationEvent(), true);
|
||||
dialog.show(getFragmentManager(), "EditEventDialog");
|
||||
}
|
||||
|
||||
/**
|
||||
* RecyclerViewAdapter to display event lists.
|
||||
*/
|
||||
public static class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
|
||||
private final List<AutomationEvent> mEventList;
|
||||
private final FragmentManager mFragmentManager;
|
||||
|
||||
public EventListAdapter(List<AutomationEvent> events, FragmentManager fragmentManager) {
|
||||
this.mEventList = events;
|
||||
this.mFragmentManager = fragmentManager;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.automation_event_item, parent, false);
|
||||
return new ViewHolder(v, parent.getContext());
|
||||
}
|
||||
|
||||
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)));
|
||||
layout.addView(iv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
final AutomationEvent event = mEventList.get(position);
|
||||
holder.eventTitle.setText(event.getTitle());
|
||||
holder.iconLayout.removeAllViews();
|
||||
|
||||
// trigger icons
|
||||
HashSet<Integer> triggerIcons = new HashSet<>();
|
||||
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.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()) {
|
||||
if (action.icon().isPresent())
|
||||
actionIcons.add(action.icon().get());
|
||||
}
|
||||
for(int res : actionIcons) {
|
||||
addImage(res, holder.context, holder.iconLayout);
|
||||
}
|
||||
|
||||
// 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
|
||||
public int getItemCount() {
|
||||
return mEventList.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
final RelativeLayout rootLayout;
|
||||
final LinearLayout iconLayout;
|
||||
final TextView eventTitle;
|
||||
final Context context;
|
||||
final ImageView iconTrash;
|
||||
|
||||
public ViewHolder(View view, Context context) {
|
||||
super(view);
|
||||
this.context = context;
|
||||
eventTitle = view.findViewById(R.id.viewEventTitle);
|
||||
rootLayout = view.findViewById(R.id.rootLayout);
|
||||
iconLayout = view.findViewById(R.id.iconLayout);
|
||||
iconTrash = view.findViewById(R.id.iconTrash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RecyclerViewAdapter to display event lists.
|
||||
*/
|
||||
public static class ActionListAdapter extends RecyclerView.Adapter<ActionListAdapter.ViewHolder> {
|
||||
private final List<Action> mActionList;
|
||||
private final FragmentManager mFragmentManager;
|
||||
|
||||
public ActionListAdapter(FragmentManager manager, List<Action> events) {
|
||||
this.mActionList = events;
|
||||
this.mFragmentManager = manager;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.automation_action_item, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
final Action action = mActionList.get(position);
|
||||
holder.actionTitle.setText(action.friendlyName());
|
||||
holder.itemRoot.setOnClickListener(v -> {
|
||||
if (action.hasDialog()) {
|
||||
EditActionDialog dialog = EditActionDialog.newInstance(action);
|
||||
dialog.show(mFragmentManager, "EditActionDialog");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mActionList.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView actionTitle;
|
||||
TextView actionDescription;
|
||||
LinearLayout itemRoot;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
super(view);
|
||||
itemRoot = view.findViewById(R.id.itemRoot);
|
||||
actionTitle = view.findViewById(R.id.viewActionTitle);
|
||||
actionDescription = view.findViewById(R.id.viewActionDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Adapter to display triggers dynamically with nested linear layouts.
|
||||
*/
|
||||
public static class TriggerListAdapter {
|
||||
private final LinearLayout mRootLayout;
|
||||
private final Context mContext;
|
||||
private final TriggerConnector mRootConnector;
|
||||
private final FragmentManager mFragmentManager;
|
||||
|
||||
public TriggerListAdapter(Context context, FragmentManager fragmentManager, LinearLayout rootLayout, TriggerConnector rootTrigger) {
|
||||
mRootLayout = rootLayout;
|
||||
mContext = context;
|
||||
mFragmentManager = fragmentManager;
|
||||
mRootConnector = rootTrigger;
|
||||
build();
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public LinearLayout getRootLayout() {
|
||||
return mRootLayout;
|
||||
}
|
||||
|
||||
public FragmentManager getFragmentManager() {
|
||||
return mFragmentManager;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
mRootLayout.removeAllViews();
|
||||
}
|
||||
|
||||
private void build() {
|
||||
for(int i = 0; i < mRootConnector.size(); ++i) {
|
||||
final Trigger trigger = mRootConnector.get(i);
|
||||
|
||||
// spinner
|
||||
if (i > 0) {
|
||||
createSpinner(trigger);
|
||||
}
|
||||
|
||||
// trigger layout
|
||||
mRootLayout.addView(trigger.createView(mContext, mFragmentManager));
|
||||
|
||||
// buttons
|
||||
createButtons(trigger);
|
||||
}
|
||||
|
||||
if (mRootConnector.size() == 0) {
|
||||
Button buttonAdd = new Button(mContext);
|
||||
buttonAdd.setText("Add New");
|
||||
buttonAdd.setOnClickListener(v -> {
|
||||
ChooseTriggerDialog dialog = ChooseTriggerDialog.newInstance();
|
||||
dialog.setOnClickListener(newTriggerObject -> {
|
||||
mRootConnector.add(newTriggerObject);
|
||||
rebuild();
|
||||
});
|
||||
dialog.show(mFragmentManager, "ChooseTriggerDialog");
|
||||
});
|
||||
mRootLayout.addView(buttonAdd);
|
||||
}
|
||||
}
|
||||
|
||||
private Spinner createSpinner() {
|
||||
Spinner spinner = new Spinner(mContext);
|
||||
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(mContext, android.R.layout.simple_spinner_item, TriggerConnector.Type.labels());
|
||||
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinner.setAdapter(spinnerArrayAdapter);
|
||||
return spinner;
|
||||
}
|
||||
|
||||
private void createSpinner(Trigger trigger) {
|
||||
final TriggerConnector connector = trigger.getConnector();
|
||||
final int initialPosition = connector.getConnectorType().ordinal();
|
||||
Spinner spinner = createSpinner();
|
||||
spinner.setSelection(initialPosition);
|
||||
spinner.setBackgroundColor(MainApp.gc(R.color.black_overlay));
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
params.setMargins(0, MainApp.dpToPx(8), 0, MainApp.dpToPx(8));
|
||||
spinner.setLayoutParams(params);
|
||||
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (position != initialPosition) {
|
||||
// conector type changed
|
||||
changeConnector(trigger, connector, TriggerConnector.Type.values()[position]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) { }
|
||||
});
|
||||
mRootLayout.addView(spinner);
|
||||
}
|
||||
|
||||
private void createButtons(Trigger trigger) {
|
||||
// do not create buttons for TriggerConnector
|
||||
if (trigger instanceof TriggerConnector) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Button Layout
|
||||
LinearLayout buttonLayout = new LinearLayout(mContext);
|
||||
buttonLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
buttonLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
mRootLayout.addView(buttonLayout);
|
||||
|
||||
// Button [-]
|
||||
Button buttonRemove = new Button(mContext);
|
||||
buttonRemove.setText("del");
|
||||
buttonRemove.setOnClickListener(v -> {
|
||||
final TriggerConnector connector = trigger.getConnector();
|
||||
connector.remove(trigger);
|
||||
connector.simplify().rebuildView();
|
||||
});
|
||||
buttonLayout.addView(buttonRemove);
|
||||
|
||||
// Button [+]
|
||||
Button buttonAdd = new Button(mContext);
|
||||
buttonAdd.setText("add");
|
||||
buttonAdd.setOnClickListener(v -> {
|
||||
ChooseTriggerDialog dialog = ChooseTriggerDialog.newInstance();
|
||||
dialog.show(mFragmentManager, "ChooseTriggerDialog");
|
||||
dialog.setOnClickListener(newTriggerObject -> {
|
||||
TriggerConnector connector = trigger.getConnector();
|
||||
connector.add(connector.pos(trigger)+1, newTriggerObject);
|
||||
connector.simplify().rebuildView();
|
||||
});
|
||||
});
|
||||
buttonLayout.addView(buttonAdd);
|
||||
|
||||
// Button [*]
|
||||
Button buttonCopy = new Button(mContext);
|
||||
buttonCopy.setText("copy");
|
||||
buttonCopy.setOnClickListener(v -> {
|
||||
TriggerConnector connector = trigger.getConnector();
|
||||
connector.add(connector.pos(trigger)+1, trigger.duplicate());
|
||||
connector.simplify().rebuildView();
|
||||
});
|
||||
buttonLayout.addView(buttonCopy);
|
||||
}
|
||||
|
||||
public static void changeConnector(final Trigger trigger, final TriggerConnector connector, final TriggerConnector.Type newConnectorType) {
|
||||
if (connector.size() > 2) {
|
||||
// split connector
|
||||
int pos = connector.pos(trigger) - 1;
|
||||
|
||||
TriggerConnector newConnector = new TriggerConnector(newConnectorType);
|
||||
|
||||
// move trigger from pos and pos+1 into new connector
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
Trigger t = connector.get(pos);
|
||||
newConnector.add(t);
|
||||
connector.remove(t);
|
||||
}
|
||||
|
||||
connector.add(pos, newConnector);
|
||||
} else {
|
||||
connector.changeConnectorType(newConnectorType);
|
||||
}
|
||||
|
||||
connector.simplify().rebuildView();
|
||||
}
|
||||
|
||||
public void rebuild() {
|
||||
destroy();
|
||||
build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.events.EventChargingState;
|
||||
import info.nightscout.androidaps.events.EventLocationChange;
|
||||
import info.nightscout.androidaps.events.EventNetworkChange;
|
||||
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.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished;
|
||||
import info.nightscout.androidaps.services.LocationService;
|
||||
|
||||
public class AutomationPlugin extends PluginBase {
|
||||
|
||||
static AutomationPlugin plugin = null;
|
||||
|
||||
public static AutomationPlugin getPlugin() {
|
||||
if (plugin == null)
|
||||
plugin = new AutomationPlugin();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private final List<AutomationEvent> automationEvents = new ArrayList<>();
|
||||
private EventLocationChange eventLocationChange;
|
||||
private EventChargingState eventChargingState;
|
||||
private EventNetworkChange eventNetworkChange;
|
||||
|
||||
private AutomationPlugin() {
|
||||
super(new PluginDescription()
|
||||
.mainType(PluginType.GENERAL)
|
||||
.fragmentClass(AutomationFragment.class.getName())
|
||||
.pluginName(R.string.automation)
|
||||
.shortName(R.string.automation_short)
|
||||
.preferencesId(R.xml.pref_automation)
|
||||
.description(R.string.automation_description)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
Context context = MainApp.instance().getApplicationContext();
|
||||
context.startService(new Intent(context, LocationService.class));
|
||||
|
||||
MainApp.bus().register(this);
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Context context = MainApp.instance().getApplicationContext();
|
||||
context.stopService(new Intent(context, LocationService.class));
|
||||
|
||||
MainApp.bus().unregister(this);
|
||||
}
|
||||
|
||||
public List<AutomationEvent> getAutomationEvents() {
|
||||
return automationEvents;
|
||||
}
|
||||
|
||||
public EventLocationChange getEventLocationChange() {
|
||||
return eventLocationChange;
|
||||
}
|
||||
|
||||
public EventChargingState getEventChargingState() {
|
||||
return eventChargingState;
|
||||
}
|
||||
|
||||
public EventNetworkChange getEventNetworkChange() {
|
||||
return eventNetworkChange;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onEventPreferenceChange(EventPreferenceChange e) {
|
||||
if (e.isChanged(R.string.key_location)) {
|
||||
Context context = MainApp.instance().getApplicationContext();
|
||||
context.stopService(new Intent(context, LocationService.class));
|
||||
context.startService(new Intent(context, LocationService.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onEventLocationChange(EventLocationChange e) {
|
||||
eventLocationChange = e;
|
||||
processActions();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onEventChargingState(EventChargingState e) {
|
||||
eventChargingState = e;
|
||||
processActions();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onEventNetworkChange(EventNetworkChange e) {
|
||||
eventNetworkChange = e;
|
||||
processActions();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onEventAutosensCalculationFinished(EventAutosensCalculationFinished e) {
|
||||
processActions();
|
||||
}
|
||||
|
||||
// TODO keepalive
|
||||
|
||||
void processActions() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
|
||||
public abstract class Action implements Cloneable {
|
||||
|
||||
public abstract int friendlyName();
|
||||
|
||||
abstract void doAction(Callback callback);
|
||||
|
||||
public void generateDialog(LinearLayout root) { }
|
||||
|
||||
public boolean hasDialog() { return false; }
|
||||
|
||||
public String toJSON() {
|
||||
JSONObject o = new JSONObject();
|
||||
try {
|
||||
o.put("type", this.getClass().getName());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return o.toString();
|
||||
}
|
||||
|
||||
public abstract Optional<Integer> icon();
|
||||
|
||||
public void copy(Action action) { }
|
||||
|
||||
@Override
|
||||
public Action clone() throws CloneNotSupportedException {
|
||||
return (Action) super.clone();
|
||||
}
|
||||
|
||||
/*package*/ Action fromJSON(String data) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public static Action instantiate(JSONObject object) {
|
||||
try {
|
||||
String type = object.getString("type");
|
||||
JSONObject data = object.optJSONObject("data");
|
||||
Class clazz = Class.forName(type);
|
||||
return ((Action) clazz.newInstance()).fromJSON(data != null ? data.toString() : "");
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||
import info.nightscout.androidaps.interfaces.PluginType;
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
|
||||
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
|
||||
public class ActionLoopDisable extends Action {
|
||||
@Override
|
||||
public int friendlyName() {
|
||||
return R.string.disableloop;
|
||||
}
|
||||
|
||||
@Override
|
||||
void doAction(Callback callback) {
|
||||
if (LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
|
||||
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, false);
|
||||
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopDisable");
|
||||
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() {
|
||||
@Override
|
||||
public void run() {
|
||||
MainApp.bus().post(new EventRefreshOverview("ActionLoopDisable"));
|
||||
if (callback != null)
|
||||
callback.result(result).run();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (callback != null)
|
||||
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadydisabled)).run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.of(R.drawable.ic_stop_24dp);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||
import info.nightscout.androidaps.interfaces.PluginType;
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
|
||||
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
|
||||
public class ActionLoopEnable extends Action {
|
||||
@Override
|
||||
public int friendlyName() {
|
||||
return R.string.enableloop;
|
||||
}
|
||||
|
||||
@Override
|
||||
void doAction(Callback callback) {
|
||||
if (!LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
|
||||
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, true);
|
||||
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopEnable");
|
||||
MainApp.bus().post(new EventRefreshOverview("ActionLoopEnable"));
|
||||
if (callback != null)
|
||||
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||
} else {
|
||||
if (callback != null)
|
||||
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadyenabled)).run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.of(R.drawable.ic_play_circle_outline_24dp);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
|
||||
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
|
||||
public class ActionLoopResume extends Action {
|
||||
@Override
|
||||
public int friendlyName() {
|
||||
return R.string.resumeloop;
|
||||
}
|
||||
|
||||
@Override
|
||||
void doAction(Callback callback) {
|
||||
if (LoopPlugin.getPlugin().isSuspended()) {
|
||||
LoopPlugin.getPlugin().suspendTo(0);
|
||||
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopResume");
|
||||
NSUpload.uploadOpenAPSOffline(0);
|
||||
MainApp.bus().post(new EventRefreshOverview("ActionLoopResume"));
|
||||
if (callback != null)
|
||||
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||
} else {
|
||||
if (callback != null)
|
||||
callback.result(new PumpEnactResult().success(true).comment(R.string.notsuspended)).run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.of(R.drawable.ic_replay_24dp);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
|
||||
public class ActionLoopSuspend extends Action {
|
||||
private int minutes;
|
||||
|
||||
@Override
|
||||
public int friendlyName() {
|
||||
return R.string.suspendloop;
|
||||
}
|
||||
|
||||
@Override
|
||||
void doAction(Callback callback) {
|
||||
if (!LoopPlugin.getPlugin().isSuspended()) {
|
||||
LoopPlugin.getPlugin().suspendLoop(minutes);
|
||||
MainApp.bus().post(new EventRefreshOverview("ActionLoopSuspend"));
|
||||
if (callback != null)
|
||||
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||
} else {
|
||||
if (callback != null)
|
||||
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadysuspended)).run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.of(R.drawable.ic_pause_circle_outline_24dp);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||
import info.nightscout.androidaps.db.Source;
|
||||
import info.nightscout.androidaps.db.TempTarget;
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.automation.elements.InputBg;
|
||||
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
|
||||
import info.nightscout.androidaps.plugins.general.automation.elements.Label;
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
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);
|
||||
|
||||
public ActionStartTempTarget() {
|
||||
value = new InputBg(Constants.MGDL);
|
||||
}
|
||||
|
||||
public ActionStartTempTarget(String units) {
|
||||
value = new InputBg(units);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int friendlyName() {
|
||||
return R.string.starttemptarget;
|
||||
}
|
||||
|
||||
@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());
|
||||
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
|
||||
if (callback != null)
|
||||
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateDialog(LinearLayout root) {
|
||||
int unitResId = value.getUnits().equals(Constants.MGDL) ? R.string.mgdl : R.string.mmol;
|
||||
Label labelBg = new Label(MainApp.gs(R.string.careportal_newnstreatment_percentage_label), MainApp.gs(unitResId), value);
|
||||
labelBg.generateDialog(root);
|
||||
Label labelDuration = new Label(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "min", duration);
|
||||
labelDuration.generateDialog(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDialog() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJSON() {
|
||||
JSONObject o = new JSONObject();
|
||||
try {
|
||||
o.put("type", ActionStartTempTarget.class.getName());
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("reason", reason);
|
||||
data.put("valueInMg", value.getMgdl());
|
||||
data.put("units", value.getUnits());
|
||||
data.put("durationInMinutes", duration.getMinutes());
|
||||
o.put("data", data);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return o.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
Action fromJSON(String data) {
|
||||
try {
|
||||
JSONObject d = new JSONObject(data);
|
||||
reason = JsonHelper.safeGetString(d, "reason");
|
||||
value.setUnits(JsonHelper.safeGetString(d, "units"));
|
||||
value.setMgdl(JsonHelper.safeGetInt(d, "valueInMg"));
|
||||
duration.setMinutes(JsonHelper.safeGetDouble(d, "durationInMinutes"));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(Action action) {
|
||||
if (action instanceof ActionStartTempTarget) {
|
||||
ActionStartTempTarget src = (ActionStartTempTarget)action;
|
||||
this.duration = src.duration;
|
||||
this.value = src.value;
|
||||
this.reason = src.reason;
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.dialogs;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
|
||||
import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopDisable;
|
||||
import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopEnable;
|
||||
import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopResume;
|
||||
import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopSuspend;
|
||||
import info.nightscout.androidaps.plugins.general.automation.actions.ActionStartTempTarget;
|
||||
|
||||
public class ChooseActionDialog extends DialogFragment {
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick(Action newActionObject);
|
||||
}
|
||||
|
||||
private static OnClickListener mClickListener = null;
|
||||
|
||||
private static final List<Action> actionDummyObjects = new ArrayList<Action>() {{
|
||||
add(new ActionLoopDisable());
|
||||
add(new ActionLoopEnable());
|
||||
add(new ActionLoopResume());
|
||||
add(new ActionLoopSuspend());
|
||||
add(new ActionStartTempTarget());
|
||||
}};
|
||||
|
||||
private Unbinder mUnbinder;
|
||||
|
||||
@BindView(R.id.radioGroup)
|
||||
RadioGroup mRadioGroup;
|
||||
|
||||
public static ChooseActionDialog newInstance() {
|
||||
Bundle args = new Bundle();
|
||||
|
||||
ChooseActionDialog fragment = new ChooseActionDialog();
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.automation_dialog_choose_action, container, false);
|
||||
mUnbinder = ButterKnife.bind(this, view);
|
||||
|
||||
for(Action a : actionDummyObjects) {
|
||||
RadioButton radioButton = new RadioButton(getContext());
|
||||
radioButton.setText(a.friendlyName());
|
||||
radioButton.setTag(a);
|
||||
mRadioGroup.addView(radioButton);
|
||||
}
|
||||
|
||||
// restore checked radio button
|
||||
int checkedIndex = 0;
|
||||
if (savedInstanceState != null) {
|
||||
checkedIndex = savedInstanceState.getInt("checkedIndex");
|
||||
}
|
||||
|
||||
((RadioButton)mRadioGroup.getChildAt(checkedIndex)).setChecked(true);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private int getCheckedIndex() {
|
||||
for(int i = 0; i < mRadioGroup.getChildCount(); ++i) {
|
||||
if (((RadioButton)mRadioGroup.getChildAt(i)).isChecked())
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private Class getActionClass() {
|
||||
int radioButtonID = mRadioGroup.getCheckedRadioButtonId();
|
||||
RadioButton radioButton = mRadioGroup.findViewById(radioButtonID);
|
||||
if (radioButton != null) {
|
||||
Object tag = radioButton.getTag();
|
||||
if (tag instanceof Action)
|
||||
return tag.getClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Action instantiateAction() {
|
||||
Class actionClass = getActionClass();
|
||||
if (actionClass != null) {
|
||||
try {
|
||||
return (Action) actionClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void setOnClickListener(OnClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mUnbinder.unbind();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@OnClick(R.id.ok)
|
||||
public void onButtonOk(View view) {
|
||||
if (mClickListener != null)
|
||||
mClickListener.onClick(instantiateAction());
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.cancel)
|
||||
public void onButtonCancel(View view) {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
bundle.putInt("checkedIndex", getCheckedIndex());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.dialogs;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerBg;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTime;
|
||||
|
||||
public class ChooseTriggerDialog extends DialogFragment {
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick(Trigger newTriggerObject);
|
||||
}
|
||||
|
||||
private static final List<Trigger> triggerDummyObjects = new ArrayList<Trigger>() {{
|
||||
add(new TriggerBg());
|
||||
add(new TriggerTime());
|
||||
}};
|
||||
|
||||
private Unbinder mUnbinder;
|
||||
private OnClickListener mClickListener = null;
|
||||
|
||||
@BindView(R.id.radioGroup)
|
||||
RadioGroup mRadioGroup;
|
||||
|
||||
public static ChooseTriggerDialog newInstance() {
|
||||
Bundle args = new Bundle();
|
||||
|
||||
ChooseTriggerDialog fragment = new ChooseTriggerDialog();
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.automation_dialog_choose_trigger, container, false);
|
||||
mUnbinder = ButterKnife.bind(this, view);
|
||||
|
||||
for(Trigger t : triggerDummyObjects) {
|
||||
RadioButton radioButton = new RadioButton(getContext());
|
||||
radioButton.setText(t.friendlyName());
|
||||
radioButton.setTag(t);
|
||||
mRadioGroup.addView(radioButton);
|
||||
}
|
||||
|
||||
// restore checked radio button
|
||||
int checkedIndex = 0;
|
||||
if (savedInstanceState != null) {
|
||||
checkedIndex = savedInstanceState.getInt("checkedIndex");
|
||||
}
|
||||
|
||||
((RadioButton)mRadioGroup.getChildAt(checkedIndex)).setChecked(true);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private int getCheckedIndex() {
|
||||
for(int i = 0; i < mRadioGroup.getChildCount(); ++i) {
|
||||
if (((RadioButton)mRadioGroup.getChildAt(i)).isChecked())
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private Class getTriggerClass() {
|
||||
int radioButtonID = mRadioGroup.getCheckedRadioButtonId();
|
||||
RadioButton radioButton = mRadioGroup.findViewById(radioButtonID);
|
||||
if (radioButton != null) {
|
||||
Object tag = radioButton.getTag();
|
||||
if (tag instanceof Trigger)
|
||||
return tag.getClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Trigger instantiateTrigger() {
|
||||
Class triggerClass = getTriggerClass();
|
||||
if (triggerClass != null) {
|
||||
try {
|
||||
return (Trigger) triggerClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void setOnClickListener(OnClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mUnbinder.unbind();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@OnClick(R.id.ok)
|
||||
public void onButtonOk(View view) {
|
||||
if (mClickListener != null)
|
||||
mClickListener.onClick(instantiateTrigger());
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.cancel)
|
||||
public void onButtonCancel(View view) {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
bundle.putInt("checkedIndex", getCheckedIndex());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.dialogs;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
|
||||
|
||||
public class EditActionDialog extends DialogFragment {
|
||||
private static Action resultAction;
|
||||
|
||||
private Unbinder mUnbinder;
|
||||
private Action mAction;
|
||||
|
||||
@BindView(R.id.layout_root)
|
||||
LinearLayout mRootLayout;
|
||||
|
||||
@BindView(R.id.viewActionTitle)
|
||||
TextView mViewActionTitle;
|
||||
|
||||
public static EditActionDialog newInstance(Action action) {
|
||||
Bundle args = new Bundle();
|
||||
EditActionDialog fragment = new EditActionDialog();
|
||||
fragment.setArguments(args);
|
||||
|
||||
// clone action to static object
|
||||
try {
|
||||
resultAction = action.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.automation_dialog_action, container, false);
|
||||
mUnbinder = ButterKnife.bind(this, view);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
String actionData = savedInstanceState.getString("action");
|
||||
if (actionData != null) {
|
||||
try {
|
||||
mAction = Action.instantiate(new JSONObject(actionData));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mAction == null)
|
||||
mAction = resultAction;
|
||||
|
||||
mViewActionTitle.setText(mAction.friendlyName());
|
||||
mRootLayout.removeAllViews();
|
||||
mAction.generateDialog(mRootLayout);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mUnbinder.unbind();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@OnClick(R.id.ok)
|
||||
public void onButtonOk(View view) {
|
||||
resultAction.copy(mAction);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.cancel)
|
||||
public void onButtonCancel(View view) {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
bundle.putString("action", mAction.toJSON());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
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.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.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
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;
|
||||
|
||||
public class EditEventDialog extends DialogFragment {
|
||||
public interface OnClickListener {
|
||||
void onClick(AutomationEvent event);
|
||||
}
|
||||
|
||||
private static OnClickListener mClickListener = null;
|
||||
private static AutomationEvent staticEvent;
|
||||
|
||||
public static void setOnClickListener(OnClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
|
||||
@BindView(R.id.inputEventTitle)
|
||||
TextInputEditText mEditEventTitle;
|
||||
|
||||
@BindView(R.id.editTrigger)
|
||||
TextView mEditTrigger;
|
||||
|
||||
@BindView(R.id.editAction)
|
||||
TextView mEditAction;
|
||||
|
||||
@BindView(R.id.triggerDescription)
|
||||
TextView mTriggerDescription;
|
||||
|
||||
@BindView(R.id.actionListView)
|
||||
RecyclerView mActionListView;
|
||||
|
||||
private Unbinder mUnbinder;
|
||||
private AutomationFragment.ActionListAdapter mActionListAdapter;
|
||||
private AutomationEvent mEvent;
|
||||
private boolean mAddNew;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// load data from bundle
|
||||
if (savedInstanceState != null) {
|
||||
String eventData = savedInstanceState.getString("event");
|
||||
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());
|
||||
|
||||
// setup trigger click event listener
|
||||
EditTriggerDialog.setOnClickListener(trigger -> {
|
||||
mEvent.setTrigger(trigger);
|
||||
mTriggerDescription.setText(mEvent.getTrigger().friendlyDescription());
|
||||
});
|
||||
mEditTrigger.setOnClickListener(v -> {
|
||||
EditTriggerDialog dialog = EditTriggerDialog.newInstance(mEvent.getTrigger());
|
||||
dialog.show(getFragmentManager(), "EditTriggerDialog");
|
||||
});
|
||||
|
||||
// setup action list view
|
||||
mActionListAdapter = new AutomationFragment.ActionListAdapter(getFragmentManager(), mEvent.getActions());
|
||||
mActionListView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mActionListView.setAdapter(mActionListAdapter);
|
||||
|
||||
// setup action click event listener
|
||||
ChooseActionDialog.setOnClickListener(newActionObject -> {
|
||||
mEvent.addAction(newActionObject);
|
||||
mActionListAdapter.notifyDataSetChanged();
|
||||
});
|
||||
mEditAction.setOnClickListener(v -> {
|
||||
ChooseActionDialog dialog = ChooseActionDialog.newInstance();
|
||||
dialog.show(getFragmentManager(), "ChooseActionDialog");
|
||||
});
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mUnbinder.unbind();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@OnClick(R.id.ok)
|
||||
public void onButtonOk(View view) {
|
||||
// check for title
|
||||
String title = mEditEventTitle.getText().toString();
|
||||
if (title.isEmpty()) {
|
||||
Toast.makeText(getContext(), R.string.automation_missing_task_name, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
mEvent.setTitle(title);
|
||||
|
||||
// check for at least one trigger
|
||||
TriggerConnector con = (TriggerConnector) mEvent.getTrigger();
|
||||
if (con.size() == 0) {
|
||||
Toast.makeText(getContext(), R.string.automation_missing_trigger, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for at least one action
|
||||
if (mEvent.getActions().isEmpty()) {
|
||||
Toast.makeText(getContext(), R.string.automation_missing_action, Toast.LENGTH_LONG).show();
|
||||
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();
|
||||
}
|
||||
|
||||
@OnClick(R.id.cancel)
|
||||
public void onButtonCancel(View view) {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
bundle.putString("event", mEvent.toJSON());
|
||||
bundle.putBoolean("addNew", mAddNew);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.dialogs;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
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.triggers.Trigger;
|
||||
|
||||
public class EditTriggerDialog extends DialogFragment {
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick(Trigger newTriggerObject);
|
||||
}
|
||||
|
||||
private static OnClickListener mClickListener = null;
|
||||
|
||||
@BindView(R.id.layoutTrigger)
|
||||
LinearLayout mLayoutTrigger;
|
||||
|
||||
private Trigger mTrigger;
|
||||
private Unbinder mUnbinder;
|
||||
|
||||
public static EditTriggerDialog newInstance(Trigger trigger) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString("trigger", trigger.toJSON());
|
||||
EditTriggerDialog fragment = new EditTriggerDialog();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.automation_dialog_edit_trigger, container, false);
|
||||
mUnbinder = ButterKnife.bind(this, view);
|
||||
|
||||
// load data from bundle
|
||||
Bundle bundle = savedInstanceState != null ? savedInstanceState : getArguments();
|
||||
if (bundle != null) {
|
||||
String triggerData = bundle.getString("trigger");
|
||||
if (triggerData != null) mTrigger = Trigger.instantiate(triggerData);
|
||||
}
|
||||
|
||||
// display root trigger
|
||||
mLayoutTrigger.addView(mTrigger.createView(getContext(), getFragmentManager()));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public static void setOnClickListener(OnClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mUnbinder.unbind();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@OnClick(R.id.ok)
|
||||
public void onButtonOk(View view) {
|
||||
if (mClickListener != null)
|
||||
mClickListener.onClick(mTrigger);
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.cancel)
|
||||
public void onButtonCancel(View view) {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
bundle.putString("trigger", mTrigger.toJSON());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
public class Element {
|
||||
public void generateDialog(LinearLayout root) { }
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.data.Profile;
|
||||
import info.nightscout.androidaps.utils.NumberPicker;
|
||||
|
||||
public class InputBg extends Element {
|
||||
final private TextWatcher textWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
// TODO: validate inputs
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
};
|
||||
|
||||
private String units;
|
||||
private double value;
|
||||
private final double minValue, maxValue, step;
|
||||
private final DecimalFormat decimalFormat;
|
||||
|
||||
public InputBg(String units) {
|
||||
this.units = units;
|
||||
|
||||
// set default initial value
|
||||
if (units.equals(Constants.MMOL)) {
|
||||
// mmol
|
||||
value = 5.5;
|
||||
minValue = 2;
|
||||
maxValue = 30;
|
||||
step = 0.1;
|
||||
decimalFormat = new DecimalFormat("0.0");
|
||||
} else {
|
||||
// mg/dL
|
||||
value = 100;
|
||||
minValue = 36;
|
||||
maxValue = 540;
|
||||
step = 1;
|
||||
decimalFormat = new DecimalFormat("0");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateDialog(LinearLayout root) {
|
||||
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
|
||||
numberPicker.setParams(0d, minValue, maxValue, step, decimalFormat, false, textWatcher);
|
||||
numberPicker.setValue(value);
|
||||
numberPicker.setOnValueChangedListener(value -> this.value = value);
|
||||
root.addView(numberPicker);
|
||||
}
|
||||
|
||||
public String getUnits() {
|
||||
return units;
|
||||
}
|
||||
|
||||
public void setUnits(String units) {
|
||||
if (!this.units.equals(units)) {
|
||||
String previousUnits = this.units;
|
||||
this.units = units;
|
||||
value = Profile.toUnits(Profile.toMgdl(value, previousUnits), Profile.toMmol(value, previousUnits), units);
|
||||
}
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getMgdl() {
|
||||
return (int)Profile.toMgdl(value, units);
|
||||
}
|
||||
|
||||
public void setMgdl(int value) {
|
||||
this.value = Profile.fromMgdlToUnits(value, units);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import info.nightscout.androidaps.utils.NumberPicker;
|
||||
|
||||
public class InputDuration extends Element {
|
||||
public enum TimeUnit {
|
||||
MINUTES,
|
||||
HOURS
|
||||
}
|
||||
|
||||
private TimeUnit unit;
|
||||
private double value;
|
||||
|
||||
public InputDuration(double value, TimeUnit unit) {
|
||||
this.unit = unit;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateDialog(LinearLayout root) {
|
||||
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
|
||||
if (unit.equals(TimeUnit.MINUTES)) {
|
||||
// Minutes
|
||||
numberPicker.setParams(0d, 0d, 24 * 60d, 10d, new DecimalFormat("0"), false);
|
||||
} else {
|
||||
// Hours
|
||||
numberPicker.setParams(0d, 0d, 24d, 1d, new DecimalFormat("0"), false);
|
||||
}
|
||||
numberPicker.setValue(value);
|
||||
numberPicker.setOnValueChangedListener(value -> this.value = value);
|
||||
root.addView(numberPicker);
|
||||
}
|
||||
|
||||
public TimeUnit getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setMinutes(double value) {
|
||||
if (unit.equals(TimeUnit.MINUTES)) {
|
||||
this.value = value;
|
||||
} else {
|
||||
this.value = value / 60d;
|
||||
}
|
||||
}
|
||||
|
||||
public double getMinutes() {
|
||||
if (unit.equals(TimeUnit.MINUTES)) {
|
||||
return value;
|
||||
} else {
|
||||
return value * 60d;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
|
||||
public class Label extends Element {
|
||||
private final Element element;
|
||||
private final String textPre;
|
||||
private final String textPost;
|
||||
|
||||
public Label(String textPre, String textPost, Element element) {
|
||||
this.element = element;
|
||||
this.textPre = textPre;
|
||||
this.textPost = textPost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateDialog(LinearLayout root) {
|
||||
// container layout
|
||||
LinearLayout layout = new LinearLayout(root.getContext());
|
||||
layout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
// text view pre element
|
||||
int px = MainApp.dpToPx(10);
|
||||
TextView textViewPre = new TextView(root.getContext());
|
||||
textViewPre.setText(textPre);
|
||||
textViewPre.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
textViewPre.setWidth(MainApp.dpToPx(120));
|
||||
textViewPre.setPadding(px, px, px, px);
|
||||
textViewPre.setTypeface(textViewPre.getTypeface(), Typeface.BOLD);
|
||||
layout.addView(textViewPre);
|
||||
|
||||
// add element to layout
|
||||
element.generateDialog(layout);
|
||||
|
||||
// text view post element
|
||||
if (textPost != null) {
|
||||
px = MainApp.dpToPx(5);
|
||||
TextView textViewPost = new TextView(root.getContext());
|
||||
textViewPost.setText(textPost);
|
||||
textViewPost.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
textViewPost.setWidth(MainApp.dpToPx(45));
|
||||
textViewPost.setPadding(px, px, px, px);
|
||||
textViewPost.setTypeface(textViewPost.getTypeface(), Typeface.BOLD);
|
||||
layout.addView(textViewPost);
|
||||
}
|
||||
|
||||
// add layout to root layout
|
||||
root.addView(layout);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
|
||||
public abstract class Trigger implements Cloneable {
|
||||
|
||||
public enum Comparator {
|
||||
IS_LOWER,
|
||||
IS_EQUAL_OR_LOWER,
|
||||
IS_EQUAL,
|
||||
IS_EQUAL_OR_GREATER,
|
||||
IS_GREATER,
|
||||
IS_NOT_AVAILABLE;
|
||||
|
||||
public @StringRes int getStringRes() {
|
||||
switch (this) {
|
||||
case IS_LOWER:
|
||||
return R.string.islower;
|
||||
case IS_EQUAL_OR_LOWER:
|
||||
return R.string.isequalorlower;
|
||||
case IS_EQUAL:
|
||||
return R.string.isequal;
|
||||
case IS_EQUAL_OR_GREATER:
|
||||
return R.string.isequalorgreater;
|
||||
case IS_GREATER:
|
||||
return R.string.isgreater;
|
||||
case IS_NOT_AVAILABLE:
|
||||
return R.string.isnotavailable;
|
||||
default:
|
||||
return R.string.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends Comparable> boolean check(T obj1, T obj2) {
|
||||
if (obj1 == null || obj2 == null)
|
||||
return this.equals(Comparator.IS_NOT_AVAILABLE);
|
||||
|
||||
int comparison = obj1.compareTo(obj2);
|
||||
switch (this) {
|
||||
case IS_LOWER:
|
||||
return comparison < 0;
|
||||
case IS_EQUAL_OR_LOWER:
|
||||
return comparison <= 0;
|
||||
case IS_EQUAL:
|
||||
return comparison == 0;
|
||||
case IS_EQUAL_OR_GREATER:
|
||||
return comparison >= 0;
|
||||
case IS_GREATER:
|
||||
return comparison > 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> labels() {
|
||||
List<String> list = new ArrayList<>();
|
||||
for(Comparator c : values()) {
|
||||
list.add(MainApp.gs(c.getStringRes()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
protected TriggerConnector connector = null;
|
||||
|
||||
Trigger() {
|
||||
}
|
||||
|
||||
public TriggerConnector getConnector() {
|
||||
return connector;
|
||||
}
|
||||
|
||||
public abstract boolean shouldRun();
|
||||
|
||||
public abstract String toJSON();
|
||||
|
||||
/*package*/ abstract Trigger fromJSON(String data);
|
||||
|
||||
public abstract int friendlyName();
|
||||
|
||||
public abstract String friendlyDescription();
|
||||
|
||||
public abstract Optional<Integer> icon();
|
||||
|
||||
void notifyAboutRun(long time) {
|
||||
}
|
||||
|
||||
public abstract Trigger duplicate();
|
||||
|
||||
public static Trigger instantiate(String json) {
|
||||
try {
|
||||
return instantiate(new JSONObject(json));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Trigger instantiate(JSONObject object) {
|
||||
try {
|
||||
String type = object.getString("type");
|
||||
JSONObject data = object.getJSONObject("data");
|
||||
Class clazz = Class.forName(type);
|
||||
return ((Trigger) clazz.newInstance()).fromJSON(data.toString());
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public View createView(Context context, FragmentManager fragmentManager) {
|
||||
final int padding = MainApp.dpToPx(4);
|
||||
|
||||
LinearLayout root = new LinearLayout(context);
|
||||
root.setPadding(padding, padding, padding, padding);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
TextView title = new TextView(context);
|
||||
title.setText(friendlyName());
|
||||
root.addView(title);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger clone() throws CloneNotSupportedException {
|
||||
Trigger t = (Trigger) super.clone();
|
||||
t.connector = connector; // parent should already be cloned
|
||||
return t;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.data.GlucoseStatus;
|
||||
import info.nightscout.androidaps.data.Profile;
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||
import info.nightscout.androidaps.utils.JsonHelper;
|
||||
import info.nightscout.androidaps.utils.NumberPicker;
|
||||
|
||||
public class TriggerBg extends Trigger {
|
||||
|
||||
private double threshold = 100.0; // FIXME
|
||||
private Comparator comparator = Comparator.IS_EQUAL;
|
||||
private String units = ProfileFunctions.getInstance().getProfileUnits();
|
||||
|
||||
final private TextWatcher textWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
// TODO: validate inputs
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
};
|
||||
|
||||
public TriggerBg() {
|
||||
super();
|
||||
}
|
||||
|
||||
private TriggerBg(TriggerBg triggerBg) {
|
||||
super();
|
||||
comparator = triggerBg.comparator;
|
||||
units = triggerBg.units;
|
||||
threshold = triggerBg.threshold;
|
||||
}
|
||||
|
||||
public double getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public Comparator getComparator() {
|
||||
return comparator;
|
||||
}
|
||||
|
||||
public String getUnits() {
|
||||
return units;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean shouldRun() {
|
||||
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
|
||||
|
||||
if (glucoseStatus == null && comparator.equals(Comparator.IS_NOT_AVAILABLE))
|
||||
return true;
|
||||
if (glucoseStatus == null)
|
||||
return false;
|
||||
|
||||
return comparator.check(glucoseStatus.glucose, Profile.toMgdl(threshold, units));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toJSON() {
|
||||
JSONObject o = new JSONObject();
|
||||
try {
|
||||
o.put("type", TriggerBg.class.getName());
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("threshold", threshold);
|
||||
data.put("comparator", comparator.toString());
|
||||
data.put("units", units);
|
||||
o.put("data", data);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return o.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
Trigger fromJSON(String data) {
|
||||
try {
|
||||
JSONObject d = new JSONObject(data);
|
||||
threshold = JsonHelper.safeGetDouble(d, "threshold");
|
||||
comparator = Comparator.valueOf(JsonHelper.safeGetString(d, "comparator"));
|
||||
units = JsonHelper.safeGetString(d, "units");
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int friendlyName() {
|
||||
return R.string.glucose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String friendlyDescription() {
|
||||
if (comparator.equals(Comparator.IS_NOT_AVAILABLE))
|
||||
return MainApp.gs(R.string.glucoseisnotavailable);
|
||||
else
|
||||
return MainApp.gs(R.string.glucosecompared, MainApp.gs(comparator.getStringRes()), threshold, units);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.of(R.drawable.icon_cp_bgcheck);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger duplicate() {
|
||||
return new TriggerBg(this);
|
||||
}
|
||||
|
||||
TriggerBg threshold(double threshold) {
|
||||
this.threshold = threshold;
|
||||
return this;
|
||||
}
|
||||
|
||||
TriggerBg comparator(Comparator comparator) {
|
||||
this.comparator = comparator;
|
||||
return this;
|
||||
}
|
||||
|
||||
TriggerBg units(String units) {
|
||||
this.units = units;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context, FragmentManager fragmentManager) {
|
||||
LinearLayout root = (LinearLayout) super.createView(context, fragmentManager);
|
||||
|
||||
// spinner for comparator
|
||||
Spinner spinner = new Spinner(context);
|
||||
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, Comparator.labels());
|
||||
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinner.setAdapter(spinnerArrayAdapter);
|
||||
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
spinnerParams.setMargins(0, MainApp.dpToPx(4), 0, MainApp.dpToPx(4));
|
||||
spinner.setLayoutParams(spinnerParams);
|
||||
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
comparator = Comparator.values()[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) { }
|
||||
});
|
||||
spinner.setSelection(comparator.ordinal());
|
||||
root.addView(spinner);
|
||||
|
||||
// horizontal layout
|
||||
LinearLayout layout = new LinearLayout(context);
|
||||
layout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
root.addView(layout);
|
||||
|
||||
// input field for threshold
|
||||
NumberPicker numberPicker = new NumberPicker(context, null);
|
||||
numberPicker.setParams(0d, 0d, (double) 500, 1d, new DecimalFormat("0"), false, textWatcher);
|
||||
numberPicker.setValue(threshold);
|
||||
numberPicker.setOnValueChangedListener(value -> threshold = value);
|
||||
layout.addView(numberPicker);
|
||||
|
||||
// text view for unit
|
||||
TextView tvUnits = new TextView(context);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
);
|
||||
params.setMargins(MainApp.dpToPx(6), 0, 0, 0);
|
||||
tvUnits.setLayoutParams(params);
|
||||
tvUnits.setText(units);
|
||||
tvUnits.setGravity(Gravity.CENTER_VERTICAL);
|
||||
layout.addView(tvUnits);
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.plugins.general.automation.AutomationFragment;
|
||||
import info.nightscout.androidaps.utils.JsonHelper;
|
||||
|
||||
public class TriggerConnector extends Trigger {
|
||||
public enum Type {
|
||||
AND,
|
||||
OR,
|
||||
XOR;
|
||||
|
||||
public boolean apply(boolean a, boolean b) {
|
||||
switch (this) {
|
||||
case AND:
|
||||
return a && b;
|
||||
case OR:
|
||||
return a || b;
|
||||
case XOR:
|
||||
return a ^ b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public @StringRes int getStringRes() {
|
||||
switch (this) {
|
||||
case OR:
|
||||
return R.string.or;
|
||||
case XOR:
|
||||
return R.string.xor;
|
||||
|
||||
default:
|
||||
case AND:
|
||||
return R.string.and;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> labels() {
|
||||
List<String> list = new ArrayList<>();
|
||||
for(Type t : values()) {
|
||||
list.add(MainApp.gs(t.getStringRes()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public static void fillIconSet(TriggerConnector connector, HashSet<Integer> set) {
|
||||
for(Trigger t : connector.list) {
|
||||
if (t instanceof TriggerConnector) {
|
||||
fillIconSet((TriggerConnector) t, set);
|
||||
} else {
|
||||
Optional<Integer> icon = t.icon();
|
||||
if (icon.isPresent()) {
|
||||
set.add(icon.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 void add(int pos, Trigger t) {
|
||||
list.add(pos, 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);
|
||||
}
|
||||
|
||||
public int pos(Trigger trigger) {
|
||||
for(int i = 0; i < list.size(); ++i) {
|
||||
if (list.get(i) == trigger) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean shouldRun() {
|
||||
boolean result = true;
|
||||
|
||||
// check first trigger
|
||||
if (list.size() > 0)
|
||||
result = list.get(0).shouldRun();
|
||||
|
||||
// check all others
|
||||
for (int i = 1; i < list.size(); ++i) {
|
||||
result = connectorType.apply(result, list.get(i).shouldRun());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public 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(" " + MainApp.gs(friendlyName()) + " ");
|
||||
result.append(t.friendlyDescription());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger duplicate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private AutomationFragment.TriggerListAdapter adapter;
|
||||
|
||||
public void rebuildView() {
|
||||
if (adapter != null)
|
||||
adapter.rebuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context, FragmentManager fragmentManager) {
|
||||
final int padding = MainApp.dpToPx(5);
|
||||
|
||||
LinearLayout root = new LinearLayout(context);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
root.setPadding(padding,padding,padding,padding);
|
||||
root.setBackgroundResource(R.drawable.border_automation_unit);
|
||||
|
||||
LinearLayout triggerListLayout = new LinearLayout(context);
|
||||
triggerListLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
triggerListLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
root.addView(triggerListLayout);
|
||||
|
||||
adapter = new AutomationFragment.TriggerListAdapter(context, fragmentManager, triggerListLayout, this);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public TriggerConnector simplify() {
|
||||
// simplify children
|
||||
for(int i = 0; i < size(); ++i) {
|
||||
if (get(i) instanceof TriggerConnector) {
|
||||
TriggerConnector t = (TriggerConnector) get(i);
|
||||
t.simplify();
|
||||
}
|
||||
}
|
||||
|
||||
// drop connector with only 1 element
|
||||
if (size() == 1 && get(0) instanceof TriggerConnector) {
|
||||
TriggerConnector c = (TriggerConnector) get(0);
|
||||
remove(c);
|
||||
changeConnectorType(c.getConnectorType());
|
||||
for (Trigger t : c.list) {
|
||||
add(t);
|
||||
}
|
||||
c.list.clear();
|
||||
return simplify();
|
||||
}
|
||||
|
||||
// merge connectors
|
||||
if (connector != null && (connector.getConnectorType().equals(connectorType) || size() == 1)) {
|
||||
final int pos = connector.pos(this);
|
||||
connector.remove(this);
|
||||
// move triggers of child connector into parent connector
|
||||
for (int i = size()-1; i >= 0; --i) {
|
||||
connector.add(pos, get(i));
|
||||
}
|
||||
list.clear();
|
||||
return connector.simplify();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,303 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.dpro.widgets.WeekdaysPicker;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.JsonHelper;
|
||||
import info.nightscout.androidaps.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];
|
||||
|
||||
private long lastRun;
|
||||
|
||||
// Single execution
|
||||
private long runAt;
|
||||
|
||||
// Recurring
|
||||
private boolean recurring;
|
||||
private int hour;
|
||||
private int minute;
|
||||
|
||||
private long validTo;
|
||||
|
||||
public TriggerTime() {
|
||||
super();
|
||||
setAll(false);
|
||||
}
|
||||
|
||||
private TriggerTime(TriggerTime triggerTime) {
|
||||
super();
|
||||
lastRun = triggerTime.lastRun;
|
||||
runAt = triggerTime.runAt;
|
||||
recurring = triggerTime.recurring;
|
||||
hour = triggerTime.hour;
|
||||
minute = triggerTime.minute;
|
||||
validTo = triggerTime.validTo;
|
||||
|
||||
for(int i = 0; i < weekdays.length; ++i) {
|
||||
weekdays[i] = triggerTime.weekdays[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void setAll(boolean value) {
|
||||
for(DayOfWeek day : DayOfWeek.values()) {
|
||||
set(day, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TriggerTime set(DayOfWeek day, boolean value) {
|
||||
weekdays[day.ordinal()] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isSet(DayOfWeek day) {
|
||||
return weekdays[day.ordinal()];
|
||||
}
|
||||
|
||||
public long getLastRun() {
|
||||
return lastRun;
|
||||
}
|
||||
|
||||
public long getRunAt() {
|
||||
return runAt;
|
||||
}
|
||||
|
||||
public boolean isRecurring() {
|
||||
return recurring;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRun() {
|
||||
if (recurring) {
|
||||
if (validTo != 0 && DateUtil.now() > validTo)
|
||||
return false;
|
||||
Calendar c = Calendar.getInstance();
|
||||
int scheduledDayOfWeek = c.get(Calendar.DAY_OF_WEEK);
|
||||
|
||||
Calendar scheduledCal = DateUtil.gregorianCalendar();
|
||||
scheduledCal.set(Calendar.HOUR_OF_DAY, hour);
|
||||
scheduledCal.set(Calendar.MINUTE, minute);
|
||||
scheduledCal.set(Calendar.SECOND, 0);
|
||||
long scheduled = scheduledCal.getTimeInMillis();
|
||||
|
||||
if (isSet(DayOfWeek.fromCalendarInt(scheduledDayOfWeek))) {
|
||||
if (DateUtil.now() >= scheduled && DateUtil.now() - scheduled < T.mins(5).msecs()) {
|
||||
if (lastRun < scheduled)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
long now = DateUtil.now();
|
||||
if (now >= runAt && now - runAt < T.mins(5).msecs())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJSON() {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONObject data = new JSONObject();
|
||||
try {
|
||||
data.put("lastRun", lastRun);
|
||||
data.put("runAt", runAt);
|
||||
data.put("recurring", recurring);
|
||||
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);
|
||||
object.put("type", TriggerTime.class.getName());
|
||||
object.put("data", data);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return object.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
Trigger fromJSON(String data) {
|
||||
JSONObject o;
|
||||
try {
|
||||
o = new JSONObject(data);
|
||||
lastRun = JsonHelper.safeGetLong(o, "lastRun");
|
||||
runAt = JsonHelper.safeGetLong(o, "runAt");
|
||||
recurring = JsonHelper.safeGetBoolean(o, "recurring");
|
||||
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");
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int friendlyName() {
|
||||
return R.string.time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String friendlyDescription() {
|
||||
if (recurring) {
|
||||
// TODO
|
||||
return "Every ";
|
||||
} else {
|
||||
return MainApp.gs(R.string.atspecifiedtime, DateUtil.dateAndTimeString(runAt));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.of(R.drawable.ic_access_alarm_24dp);
|
||||
}
|
||||
|
||||
@Override
|
||||
void notifyAboutRun(long time) {
|
||||
lastRun = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger duplicate() {
|
||||
return new TriggerTime(this);
|
||||
}
|
||||
|
||||
TriggerTime lastRun(long lastRun) {
|
||||
this.lastRun = lastRun;
|
||||
return this;
|
||||
}
|
||||
|
||||
TriggerTime runAt(long runAt) {
|
||||
this.runAt = runAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
TriggerTime recurring(boolean recurring) {
|
||||
this.recurring = recurring;
|
||||
return this;
|
||||
}
|
||||
|
||||
TriggerTime validTo(long validTo) {
|
||||
this.validTo = validTo;
|
||||
return this;
|
||||
}
|
||||
|
||||
TriggerTime hour(int hour) {
|
||||
this.hour = hour;
|
||||
return this;
|
||||
}
|
||||
|
||||
TriggerTime minute(int minute) {
|
||||
this.minute = minute;
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<Integer> getSelectedDays() {
|
||||
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());
|
||||
}
|
||||
return selectedDays;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context, FragmentManager fragmentManager) {
|
||||
LinearLayout root = (LinearLayout) super.createView(context, fragmentManager);
|
||||
|
||||
// TODO: Replace external tool WeekdaysPicker with a self-made GUI element
|
||||
WeekdaysPicker weekdaysPicker = new WeekdaysPicker(context);
|
||||
weekdaysPicker.setEditable(true);
|
||||
weekdaysPicker.setSelectedDays(getSelectedDays());
|
||||
weekdaysPicker.setOnWeekdaysChangeListener((view, i, list) -> set(DayOfWeek.fromCalendarInt(i), list.contains(i)));
|
||||
weekdaysPicker.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
root.addView(weekdaysPicker);
|
||||
return root;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package info.nightscout.androidaps.services;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.events.EventLocationChange;
|
||||
import info.nightscout.androidaps.logging.L;
|
||||
import info.nightscout.androidaps.utils.SP;
|
||||
|
||||
public class LocationService extends Service {
|
||||
private static Logger log = LoggerFactory.getLogger(L.LOCATION);
|
||||
|
||||
private LocationManager mLocationManager = null;
|
||||
private static final int LOCATION_INTERVAL = 1000;
|
||||
private static final float LOCATION_DISTANCE = 10f;
|
||||
|
||||
public LocationService() {
|
||||
MainApp.bus().register(this);
|
||||
}
|
||||
|
||||
private class LocationListener implements android.location.LocationListener {
|
||||
Location mLastLocation;
|
||||
|
||||
public LocationListener(String provider) {
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("LocationListener " + provider);
|
||||
mLastLocation = new Location(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("onLocationChanged: " + location);
|
||||
mLastLocation.set(location);
|
||||
MainApp.bus().post(new EventLocationChange(location));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("onProviderDisabled: " + provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("onProviderEnabled: " + provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("onStatusChanged: " + provider);
|
||||
}
|
||||
}
|
||||
|
||||
LocationListener mLocationListener = new LocationListener(LocationManager.PASSIVE_PROVIDER);
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent arg0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("onStartCommand");
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("onCreate");
|
||||
|
||||
initializeLocationManager();
|
||||
|
||||
try {
|
||||
if (SP.getString(R.string.key_location, "NONE").equals("NETWORK"))
|
||||
mLocationManager.requestLocationUpdates(
|
||||
LocationManager.NETWORK_PROVIDER,
|
||||
LOCATION_INTERVAL,
|
||||
LOCATION_DISTANCE,
|
||||
mLocationListener
|
||||
);
|
||||
if (SP.getString(R.string.key_location, "NONE").equals("GPS"))
|
||||
mLocationManager.requestLocationUpdates(
|
||||
LocationManager.GPS_PROVIDER,
|
||||
LOCATION_INTERVAL,
|
||||
LOCATION_DISTANCE,
|
||||
mLocationListener
|
||||
);
|
||||
if (SP.getString(R.string.key_location, "NONE").equals("PASSIVE"))
|
||||
mLocationManager.requestLocationUpdates(
|
||||
LocationManager.PASSIVE_PROVIDER,
|
||||
LOCATION_INTERVAL,
|
||||
LOCATION_DISTANCE,
|
||||
mLocationListener
|
||||
);
|
||||
} catch (java.lang.SecurityException ex) {
|
||||
log.error("fail to request location update, ignore", ex);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.error("network provider does not exist, " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("onDestroy");
|
||||
super.onDestroy();
|
||||
MainApp.bus().unregister(this);
|
||||
if (mLocationManager != null) {
|
||||
try {
|
||||
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
return;
|
||||
}
|
||||
mLocationManager.removeUpdates(mLocationListener);
|
||||
} catch (Exception ex) {
|
||||
log.error("fail to remove location listener, ignore", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeLocationManager() {
|
||||
if (L.isEnabled(L.LOCATION))
|
||||
log.debug("initializeLocationManager - LOCATION_INTERVAL: " + LOCATION_INTERVAL + " LOCATION_DISTANCE: " + LOCATION_DISTANCE);
|
||||
if (mLocationManager == null) {
|
||||
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -187,4 +187,8 @@ public class DateUtil {
|
|||
long diff = Math.abs(date - now());
|
||||
return diff < T.mins(2).msecs();
|
||||
}
|
||||
|
||||
public static GregorianCalendar gregorianCalendar() {
|
||||
return new GregorianCalendar();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
|||
View.OnTouchListener, View.OnClickListener {
|
||||
private static Logger log = LoggerFactory.getLogger(NumberPicker.class);
|
||||
|
||||
public interface OnValueChangedListener {
|
||||
void onValueChanged(double value);
|
||||
}
|
||||
|
||||
TextView editText;
|
||||
Button minusButton;
|
||||
Button plusButton;
|
||||
|
@ -48,6 +52,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
|||
|
||||
private Handler mHandler;
|
||||
private ScheduledExecutorService mUpdater;
|
||||
private OnValueChangedListener mOnValueChangedListener;
|
||||
|
||||
private class UpdateCounterTask implements Runnable {
|
||||
private boolean mInc;
|
||||
|
@ -139,10 +144,15 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
|||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
value = SafeParse.stringToDouble(editText.getText().toString());
|
||||
callValueChangedListener();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) {
|
||||
mOnValueChangedListener = onValueChangedListener;
|
||||
}
|
||||
|
||||
public void setTextWatcher(TextWatcher textWatcher) {
|
||||
this.textWatcher = textWatcher;
|
||||
editText.addTextChangedListener(textWatcher);
|
||||
|
@ -164,6 +174,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
|||
this.step = step;
|
||||
this.formater = formater;
|
||||
this.allowZero = allowZero;
|
||||
callValueChangedListener();
|
||||
|
||||
editText.setKeyListener(DigitsKeyListener.getInstance(minValue < 0, step != Math.rint(step)));
|
||||
|
||||
|
@ -178,6 +189,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
|||
if (textWatcher != null)
|
||||
editText.removeTextChangedListener(textWatcher);
|
||||
this.value = value;
|
||||
callValueChangedListener();
|
||||
updateEditText();
|
||||
if (textWatcher != null)
|
||||
editText.addTextChangedListener(textWatcher);
|
||||
|
@ -199,6 +211,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
|||
value += step * multiplier;
|
||||
if (value > maxValue) {
|
||||
value = maxValue;
|
||||
callValueChangedListener();
|
||||
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit));
|
||||
stopUpdating();
|
||||
}
|
||||
|
@ -209,6 +222,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
|||
value -= step * multiplier;
|
||||
if (value < minValue) {
|
||||
value = minValue;
|
||||
callValueChangedListener();
|
||||
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit));
|
||||
stopUpdating();
|
||||
}
|
||||
|
@ -222,6 +236,11 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
|||
editText.setText(formater.format(value));
|
||||
}
|
||||
|
||||
private void callValueChangedListener() {
|
||||
if (mOnValueChangedListener != null)
|
||||
mOnValueChangedListener.onValueChanged(value);
|
||||
}
|
||||
|
||||
private void startUpdating(boolean inc) {
|
||||
if (mUpdater != null) {
|
||||
log.debug("Another executor is still active");
|
||||
|
|
43
app/src/main/java/info/nightscout/utils/MidnightTime.java
Normal file
43
app/src/main/java/info/nightscout/utils/MidnightTime.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package info.nightscout.utils;
|
||||
|
||||
import android.util.LongSparseArray;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class MidnightTime {
|
||||
private static LongSparseArray times = new LongSparseArray();
|
||||
|
||||
private static long hits = 0;
|
||||
private static long misses = 0;
|
||||
|
||||
public static long calc() {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.set(Calendar.HOUR_OF_DAY, 0);
|
||||
c.set(Calendar.MINUTE, 0);
|
||||
c.set(Calendar.SECOND, 0);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
return c.getTimeInMillis();
|
||||
}
|
||||
|
||||
public static long calc(long time) {
|
||||
Long m = (Long) times.get(time);
|
||||
if (m != null) {
|
||||
++hits;
|
||||
return m;
|
||||
}
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTimeInMillis(time);
|
||||
c.set(Calendar.HOUR_OF_DAY, 0);
|
||||
c.set(Calendar.MINUTE, 0);
|
||||
c.set(Calendar.SECOND, 0);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
m = c.getTimeInMillis();
|
||||
times.append(time, m);
|
||||
++misses;
|
||||
return m;
|
||||
}
|
||||
|
||||
public static String log() {
|
||||
return "Hits: " + hits + " misses: " + misses + " stored: " + times.size();
|
||||
}
|
||||
}
|
7
app/src/main/res/drawable/border_automation_unit.xml
Normal file
7
app/src/main/res/drawable/border_automation_unit.xml
Normal 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="3dip"
|
||||
android:color="@android:color/white" />
|
||||
</shape>
|
9
app/src/main/res/drawable/ic_access_alarm_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_access_alarm_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#72a8ff"
|
||||
android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8L11,8v6l4.75,2.85 0.75,-1.23 -4,-2.37L12.5,8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_add_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_add_black_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="@color/ribbonWarning"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M9,16h2L11,8L9,8v8zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM13,16h2L15,8h-2v8z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FF00FF00"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M10,16.5l6,-4.5 -6,-4.5v9zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_replay_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_replay_24dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FF00FF00"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_stop_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_stop_24dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="@color/ribbonCritical"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M6,6h12v12H6z"/>
|
||||
</vector>
|
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>
|
32
app/src/main/res/layout/automation_action_item.xml
Normal file
32
app/src/main/res/layout/automation_action_item.xml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/itemRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/ribbonDefault">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/viewActionTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/viewActionDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
39
app/src/main/res/layout/automation_dialog_action.xml
Normal file
39
app/src/main/res/layout/automation_dialog_action.xml
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?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:minWidth="300dp"
|
||||
android:padding="10dp">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/viewActionTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:paddingBottom="10dp"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
|
||||
<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:id="@+id/layout_root" />
|
||||
|
||||
<include layout="@layout/mdtp_done_button" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
36
app/src/main/res/layout/automation_dialog_choose_action.xml
Normal file
36
app/src/main/res/layout/automation_dialog_choose_action.xml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?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">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<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">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Please choose an action type:"/>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/radioGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginVertical="5dp"/>
|
||||
|
||||
<include layout="@layout/mdtp_done_button" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</FrameLayout>
|
36
app/src/main/res/layout/automation_dialog_choose_trigger.xml
Normal file
36
app/src/main/res/layout/automation_dialog_choose_trigger.xml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?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">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<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">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Please choose a trigger type:"/>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/radioGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginVertical="5dp"/>
|
||||
|
||||
<include layout="@layout/mdtp_done_button" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</FrameLayout>
|
99
app/src/main/res/layout/automation_dialog_edit_action.xml
Normal file
99
app/src/main/res/layout/automation_dialog_edit_action.xml
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?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">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<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" />
|
||||
|
||||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="if:"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/editTrigger"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="Edit" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/triggerDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="then:"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/editAction"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="Edit" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actionDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<include layout="@layout/mdtp_done_button" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</FrameLayout>
|
38
app/src/main/res/layout/automation_dialog_edit_trigger.xml
Normal file
38
app/src/main/res/layout/automation_dialog_edit_trigger.xml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?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">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<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">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Triggers:"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutTrigger"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<include layout="@layout/mdtp_done_button" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</FrameLayout>
|
102
app/src/main/res/layout/automation_dialog_event.xml
Normal file
102
app/src/main/res/layout/automation_dialog_event.xml
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?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">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<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="Task Name" />
|
||||
|
||||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="if:"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/editTrigger"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:padding="8dp"
|
||||
android:background="@color/ribbonDefault"
|
||||
android:text="Edit" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/triggerDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="then:"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/editAction"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:padding="8dp"
|
||||
android:background="@color/ribbonDefault"
|
||||
android:text="Add" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/actionListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"/>
|
||||
|
||||
<include layout="@layout/mdtp_done_button" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</FrameLayout>
|
47
app/src/main/res/layout/automation_event_item.xml
Normal file
47
app/src/main/res/layout/automation_event_item.xml
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/rootLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:padding="8dp"
|
||||
android:background="@color/ribbonDefault">
|
||||
|
||||
<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: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:orientation="horizontal" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
33
app/src/main/res/layout/automation_fragment.xml
Normal file
33
app/src/main/res/layout/automation_fragment.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?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="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
tools:context="info.nightscout.androidaps.plugins.general.automation.AutomationFragment">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/eventListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fabAddEvent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:src="@drawable/ic_add_black_24dp"
|
||||
android:backgroundTint="@color/defaulttext"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</FrameLayout>
|
|
@ -118,6 +118,18 @@
|
|||
<item>@string/yes</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="location">
|
||||
<item>@string/use_passive_location</item>
|
||||
<item>@string/use_network_location</item>
|
||||
<item>@string/use_gps_location</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="locationValues" translatable="false">
|
||||
<item>PASSIVE</item>
|
||||
<item>NETWORK</item>
|
||||
<item>GPS</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="virtualPumpTypes">
|
||||
<item>Generic AAPS</item>
|
||||
<item>Accu-Chek Spirit</item>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<resources>
|
||||
<resources>
|
||||
<string name="treatmentssafety_title">Treatments safety</string>
|
||||
<string name="treatmentssafety_maxbolus_title">Max allowed bolus [U]</string>
|
||||
<string name="treatmentssafety_maxcarbs_title">Max allowed carbs [g]</string>
|
||||
|
@ -586,6 +586,7 @@
|
|||
<string name="loopsuspended">Loop suspended</string>
|
||||
<string name="loopsuspendedfor">Suspended (%1$d m)</string>
|
||||
<string name="loopsuperbolusfor">Superbolus (%1$d m)</string>
|
||||
<string name="suspendloop">Suspend loop</string>
|
||||
<string name="suspendloopfor1h">Suspend loop for 1h</string>
|
||||
<string name="suspendloopfor2h">Suspend loop for 2h</string>
|
||||
<string name="suspendloopfor3h">Suspend loop for 3h</string>
|
||||
|
@ -1305,6 +1306,50 @@
|
|||
<string name="min_recovery_duration">Min. recovery duration [s]</string>
|
||||
<string name="recovery_duration">Recovery duration</string>
|
||||
<string name="timeout_during_handshake">Timeout during handshake - reset bluetooth</string>
|
||||
<string name="weekday_sunday_short">Sun</string>
|
||||
<string name="weekday_saturday_short">Sat</string>
|
||||
<string name="weekday_friday_short">Fri</string>
|
||||
<string name="weekday_thursday_short">Thu</string>
|
||||
<string name="weekday_wednesday_short">Wed</string>
|
||||
<string name="weekday_tuesday_short">Tue</string>
|
||||
<string name="weekday_monday_short">Mon</string>
|
||||
<string name="weekday_sunday">Sunday</string>
|
||||
<string name="weekday_saturday">Saturday</string>
|
||||
<string name="weekday_friday">Friday</string>
|
||||
<string name="weekday_thursday">Thursday</string>
|
||||
<string name="weekday_wednesday">Wednesday</string>
|
||||
<string name="weekday_tuesday">Tuesday</string>
|
||||
<string name="weekday_monday">Monday</string>
|
||||
<string name="automation_description">User defined automation tasks</string>
|
||||
<string name="automation_missing_task_name">Please enter a task name.</string>
|
||||
<string name="automation_missing_trigger">Please specify at least one trigger.</string>
|
||||
<string name="automation_missing_action">Please specify at least one action.</string>
|
||||
<string name="alreadyenabled">Already enabled</string>
|
||||
<string name="alreadydisabled">Already disabled</string>
|
||||
<string name="alreadysuspended">Already suspended</string>
|
||||
<string name="resumeloop">Resume loop</string>
|
||||
<string name="notsuspended">Not suspended</string>
|
||||
<string name="starttemptarget">Start temp target</string>
|
||||
<string name="islower">is lower than</string>
|
||||
<string name="isequalorlower">is equal or lower than</string>
|
||||
<string name="isequal">is equal to</string>
|
||||
<string name="isequalorgreater">is equal or greater than</string>
|
||||
<string name="isgreater">is greater than</string>
|
||||
<string name="isnotavailable">is not available</string>
|
||||
<string name="unknown">unknown</string>
|
||||
<string name="glucoseisnotavailable">Glucose is not available</string>
|
||||
<string name="glucosecompared">Glucose %1$s %2$.2f %3$s</string>
|
||||
<string name="and">And</string>
|
||||
<string name="or">Or</string>
|
||||
<string name="xor">Exclusive or</string>
|
||||
<string name="atspecifiedtime">At %1$s</string>
|
||||
<string name="use_network_location">Use network location</string>
|
||||
<string name="use_gps_location">Use GPS location</string>
|
||||
<string name="use_passive_location">Use passive location</string>
|
||||
<string name="locationservice">Location service</string>
|
||||
<string name="key_location" translatable="false">location</string>
|
||||
<string name="automation_short">Auto</string>
|
||||
<string name="automation">Automation</string>
|
||||
|
||||
<string name="profile_total">== ∑ %1$s U</string>
|
||||
<string name="profile_ins_units_per_hout">U/h</string>
|
||||
|
|
10
app/src/main/res/xml/pref_automation.xml
Normal file
10
app/src/main/res/xml/pref_automation.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:validate="http://schemas.android.com/apk/res-auto">
|
||||
<ListPreference
|
||||
android:title="@string/locationservice"
|
||||
android:defaultValue="PASSIVE"
|
||||
android:entries="@array/location"
|
||||
android:entryValues="@array/locationValues"
|
||||
android:key="@string/key_location" />
|
||||
</PreferenceScreen>
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation;
|
||||
|
||||
import junit.framework.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.triggers.DummyTrigger;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({})
|
||||
public class ComposeTriggerTest {
|
||||
@Test
|
||||
public void testTriggerList() {
|
||||
TriggerConnector root = new TriggerConnector();
|
||||
|
||||
// add some triggers
|
||||
Trigger t0 = new DummyTrigger();
|
||||
root.add(t0);
|
||||
Trigger t1 = new DummyTrigger();
|
||||
root.add(t1);
|
||||
Trigger t2 = new DummyTrigger();
|
||||
root.add(t2);
|
||||
|
||||
Assert.assertEquals(3, root.size());
|
||||
Assert.assertEquals(t0, root.get(0));
|
||||
Assert.assertEquals(t1, root.get(1));
|
||||
Assert.assertEquals(t2, root.get(2));
|
||||
|
||||
// remove a trigger
|
||||
root.remove(t1);
|
||||
|
||||
Assert.assertEquals(2, root.size());
|
||||
Assert.assertEquals(t0, root.get(0));
|
||||
Assert.assertEquals(t2, root.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeConnector() {
|
||||
// initialize scenario
|
||||
TriggerConnector root = new TriggerConnector(TriggerConnector.Type.AND);
|
||||
Trigger t[] = new Trigger[4];
|
||||
for(int i = 0; i < t.length; ++i) {
|
||||
t[i] = new DummyTrigger();
|
||||
root.add(t[i]);
|
||||
}
|
||||
Assert.assertEquals(4, root.size());
|
||||
|
||||
// change connector of t1,t2 from "and" to "or"
|
||||
Assert.assertEquals(root, t[2].getConnector());
|
||||
AutomationFragment.TriggerListAdapter.changeConnector(t[2], t[2].getConnector(), TriggerConnector.Type.OR);
|
||||
|
||||
Assert.assertEquals(3, root.size());
|
||||
Assert.assertEquals(t[0], root.get(0));
|
||||
Assert.assertEquals(t[3], root.get(2));
|
||||
Assert.assertTrue(root.get(1) instanceof TriggerConnector);
|
||||
|
||||
TriggerConnector newConnector = (TriggerConnector) root.get(1);
|
||||
Assert.assertEquals(2, newConnector.size());
|
||||
Assert.assertEquals(t[1], newConnector.get(0));
|
||||
Assert.assertEquals(t[2], newConnector.get(1));
|
||||
|
||||
// undo
|
||||
Assert.assertEquals(newConnector, t[2].getConnector());
|
||||
AutomationFragment.TriggerListAdapter.changeConnector(t[2], t[2].getConnector(), TriggerConnector.Type.AND);
|
||||
Assert.assertEquals(4, root.size());
|
||||
for(int i = 0; i < 4; ++i) {
|
||||
Assert.assertEquals(t[i], root.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectorSimplify() {
|
||||
// initialize scenario
|
||||
/*
|
||||
* parent
|
||||
* -> child
|
||||
* -> t0
|
||||
* -> t1
|
||||
*/
|
||||
TriggerConnector parent = new TriggerConnector(TriggerConnector.Type.AND);
|
||||
TriggerConnector child = new TriggerConnector(TriggerConnector.Type.AND);
|
||||
Trigger t0 = new DummyTrigger();
|
||||
Trigger t1 = new DummyTrigger();
|
||||
child.add(t0);
|
||||
child.add(t1);
|
||||
parent.add(child);
|
||||
Assert.assertEquals(1, parent.size());
|
||||
Assert.assertEquals(child, parent.get(0));
|
||||
Assert.assertEquals(2, child.size());
|
||||
Assert.assertEquals(t0, child.get(0));
|
||||
Assert.assertEquals(t1, child.get(1));
|
||||
|
||||
// simplify
|
||||
parent.simplify();
|
||||
|
||||
/*
|
||||
* parent
|
||||
* -> t0
|
||||
* -> t1
|
||||
*/
|
||||
Assert.assertEquals(2, parent.size());
|
||||
Assert.assertEquals(t0, parent.get(0));
|
||||
Assert.assertEquals(t1, parent.get(1));
|
||||
|
||||
// add a new child at position 1
|
||||
/*
|
||||
* parent
|
||||
* -> t0
|
||||
* -> newChild
|
||||
* -> t2
|
||||
* -> t1
|
||||
*/
|
||||
TriggerConnector newChild = new TriggerConnector(TriggerConnector.Type.AND);
|
||||
Trigger t2 = new DummyTrigger();
|
||||
newChild.add(t2);
|
||||
parent.add(1, newChild);
|
||||
|
||||
// simplify
|
||||
parent.simplify();
|
||||
|
||||
/*
|
||||
* parent
|
||||
* -> t0
|
||||
* -> t2
|
||||
* -> t1
|
||||
*/
|
||||
Assert.assertEquals(3, parent.size());
|
||||
Assert.assertEquals(t0, parent.get(0));
|
||||
Assert.assertEquals(t2, parent.get(1));
|
||||
Assert.assertEquals(t1, parent.get(2));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
public class DummyTrigger extends Trigger {
|
||||
private boolean result;
|
||||
|
||||
public DummyTrigger() {
|
||||
this.result = false;
|
||||
}
|
||||
|
||||
public DummyTrigger(boolean result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRun() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJSON() { return null; }
|
||||
|
||||
@Override
|
||||
Trigger fromJSON(String data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int friendlyName() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String friendlyDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> icon() {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger duplicate() { return new DummyTrigger(result); }
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||
|
||||
import com.squareup.otto.Bus;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import info.AAPSMocker;
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.db.BgReading;
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.T;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({MainApp.class, Bus.class, ProfileFunctions.class, DateUtil.class})
|
||||
public class TriggerBgTest {
|
||||
|
||||
@Test
|
||||
public void shouldRunTest() {
|
||||
when(MainApp.getDbHelper().getBgreadingsDataFromTime(anyLong(), anyBoolean())).thenReturn(generateOneCurrentRecordBgData());
|
||||
|
||||
TriggerBg t = new TriggerBg().units(Constants.MMOL).threshold(4.1d).comparator(Trigger.Comparator.IS_EQUAL);
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(214).comparator(Trigger.Comparator.IS_EQUAL);
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(214).comparator(Trigger.Comparator.IS_EQUAL_OR_GREATER);
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(214).comparator(Trigger.Comparator.IS_EQUAL_OR_LOWER);
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(215).comparator(Trigger.Comparator.IS_EQUAL);
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(215).comparator(Trigger.Comparator.IS_EQUAL_OR_LOWER);
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(215).comparator(Trigger.Comparator.IS_EQUAL_OR_GREATER);
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(213).comparator(Trigger.Comparator.IS_EQUAL_OR_GREATER);
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(213).comparator(Trigger.Comparator.IS_EQUAL_OR_LOWER);
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
|
||||
when(MainApp.getDbHelper().getBgreadingsDataFromTime(anyLong(), anyBoolean())).thenReturn(new ArrayList<>());
|
||||
t = new TriggerBg().units(Constants.MGDL).threshold(213).comparator(Trigger.Comparator.IS_EQUAL_OR_LOWER);
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
t = new TriggerBg().comparator(Trigger.Comparator.IS_NOT_AVAILABLE);
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
}
|
||||
|
||||
String bgJson = "{\"data\":{\"comparator\":\"IS_EQUAL\",\"threshold\":4.1,\"units\":\"mmol\"},\"type\":\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerBg\"}";
|
||||
|
||||
@Test
|
||||
public void toJSONTest() {
|
||||
TriggerBg t = new TriggerBg().units(Constants.MMOL).threshold(4.1d).comparator(Trigger.Comparator.IS_EQUAL);
|
||||
Assert.assertEquals(bgJson, t.toJSON());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromJSONTest() throws JSONException {
|
||||
TriggerBg t = new TriggerBg().units(Constants.MMOL).threshold(4.1d).comparator(Trigger.Comparator.IS_EQUAL);
|
||||
|
||||
TriggerBg t2 = (TriggerBg) Trigger.instantiate(new JSONObject(t.toJSON()));
|
||||
Assert.assertEquals(Trigger.Comparator.IS_EQUAL, t2.getComparator());
|
||||
Assert.assertEquals(4.1d, t2.getThreshold(), 0.01d);
|
||||
Assert.assertEquals(Constants.MMOL, t2.getUnits());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void mock() {
|
||||
AAPSMocker.mockMainApp();
|
||||
AAPSMocker.mockBus();
|
||||
AAPSMocker.mockDatabaseHelper();
|
||||
AAPSMocker.mockProfileFunctions();
|
||||
|
||||
PowerMockito.mockStatic(DateUtil.class);
|
||||
when(DateUtil.now()).thenReturn(1514766900000L + T.mins(1).msecs());
|
||||
|
||||
}
|
||||
|
||||
List<BgReading> generateOneCurrentRecordBgData() {
|
||||
List<BgReading> list = new ArrayList<>();
|
||||
try {
|
||||
list.add(new BgReading(new NSSgv(new JSONObject("{\"mgdl\":214,\"mills\":1514766900000,\"direction\":\"Flat\"}"))));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
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;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({})
|
||||
public class TriggerConnectorTest {
|
||||
|
||||
@Test
|
||||
public void testTriggerList() {
|
||||
TriggerConnector t = new TriggerConnector();
|
||||
TriggerConnector t2 = new TriggerConnector();
|
||||
TriggerConnector t3 = new TriggerConnector();
|
||||
|
||||
Assert.assertTrue(t.size() == 0);
|
||||
|
||||
t.add(t2);
|
||||
Assert.assertTrue(t.size() == 1);
|
||||
Assert.assertEquals(t2, t.get(0));
|
||||
|
||||
t.add(t3);
|
||||
Assert.assertTrue(t.size() == 2);
|
||||
Assert.assertEquals(t2, t.get(0));
|
||||
Assert.assertEquals(t3, t.get(1));
|
||||
|
||||
Assert.assertTrue(t.remove(t2));
|
||||
Assert.assertTrue(t.size() == 1);
|
||||
Assert.assertEquals(t3, t.get(0));
|
||||
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTriggerOR() {
|
||||
TriggerConnector t = new TriggerConnector(TriggerConnector.Type.OR);
|
||||
t.add(new DummyTrigger(false));
|
||||
t.add(new DummyTrigger(false));
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
|
||||
t.add(new DummyTrigger(true));
|
||||
t.add(new DummyTrigger(false));
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTriggerXOR() {
|
||||
TriggerConnector t = new TriggerConnector(TriggerConnector.Type.XOR);
|
||||
t.add(new DummyTrigger(false));
|
||||
t.add(new DummyTrigger(false));
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
|
||||
t.add(new DummyTrigger(true));
|
||||
t.add(new DummyTrigger(false));
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
|
||||
t.add(new DummyTrigger(true));
|
||||
t.add(new DummyTrigger(false));
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTriggerAND() {
|
||||
TriggerConnector t = new TriggerConnector(TriggerConnector.Type.AND);
|
||||
t.add(new DummyTrigger(true));
|
||||
t.add(new DummyTrigger(true));
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
|
||||
t.add(new DummyTrigger(true));
|
||||
t.add(new DummyTrigger(false));
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
}
|
||||
|
||||
static public final String empty = "{\"data\":{\"connectorType\":\"AND\",\"triggerList\":[]},\"type\":\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector\"}";
|
||||
static public final String oneItem = "{\"data\":{\"connectorType\":\"AND\",\"triggerList\":[\"{\\\"data\\\":{\\\"connectorType\\\":\\\"AND\\\",\\\"triggerList\\\":[]},\\\"type\\\":\\\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector\\\"}\"]},\"type\":\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector\"}";
|
||||
|
||||
@Test
|
||||
public void toJSONTest() {
|
||||
TriggerConnector t = new TriggerConnector();
|
||||
Assert.assertEquals(empty, t.toJSON());
|
||||
t.add(new TriggerConnector());
|
||||
Assert.assertEquals(oneItem, t.toJSON());
|
||||
}
|
||||
@Test
|
||||
public void fromJSONTest() throws JSONException {
|
||||
TriggerConnector t = new TriggerConnector();
|
||||
t.add(new TriggerConnector());
|
||||
|
||||
TriggerConnector t2 = (TriggerConnector) Trigger.instantiate(new JSONObject(t.toJSON()));
|
||||
Assert.assertEquals(1, t2.size());
|
||||
Assert.assertTrue(t2.get(0) instanceof TriggerConnector);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||
|
||||
import com.squareup.otto.Bus;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import info.AAPSMocker;
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.T;
|
||||
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({MainApp.class, Bus.class, DateUtil.class, GregorianCalendar.class})
|
||||
public class TriggerTimeTest {
|
||||
|
||||
long now = 1514766900000L;
|
||||
|
||||
@Test
|
||||
public void shouldRunTest() {
|
||||
|
||||
// scheduled 1 min before
|
||||
TriggerTime t = new TriggerTime().runAt(now - T.mins(1).msecs());
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
|
||||
// scheduled 1 min in the future
|
||||
t = new TriggerTime().runAt(now + T.mins(1).msecs());
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
|
||||
// limit by validTo
|
||||
t = new TriggerTime().recurring(true).hour(1).minute(34).validTo(1);
|
||||
t.setAll(true);
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
|
||||
// scheduled 1 min before
|
||||
t = new TriggerTime().recurring(true).hour(1).minute(34);
|
||||
t.setAll(true);
|
||||
Assert.assertTrue(t.shouldRun());
|
||||
|
||||
// already run
|
||||
t = new TriggerTime().recurring(true).hour(1).minute(34).lastRun(now - 1);
|
||||
t.setAll(true);
|
||||
Assert.assertFalse(t.shouldRun());
|
||||
|
||||
}
|
||||
|
||||
String timeJson = "{\"data\":{\"runAt\":1514766840000,\"THURSDAY\":false,\"lastRun\":0,\"SUNDAY\":false,\"recurring\":false,\"TUESDAY\":false,\"FRIDAY\":false,\"minute\":0,\"WEDNESDAY\":false,\"MONDAY\":false,\"hour\":0,\"SATURDAY\":false,\"validTo\":0},\"type\":\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTime\"}";
|
||||
|
||||
@Test
|
||||
public void toJSONTest() {
|
||||
TriggerTime t = new TriggerTime().runAt(now - T.mins(1).msecs());
|
||||
Assert.assertEquals(timeJson, t.toJSON());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromJSONTest() throws JSONException {
|
||||
TriggerTime t = new TriggerTime().runAt(now - T.mins(1).msecs());
|
||||
|
||||
TriggerTime t2 = (TriggerTime) Trigger.instantiate(new JSONObject(t.toJSON()));
|
||||
Assert.assertEquals(now - T.mins(1).msecs(), t2.getRunAt());
|
||||
Assert.assertEquals(false, t2.isRecurring());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void mock() {
|
||||
AAPSMocker.mockMainApp();
|
||||
AAPSMocker.mockBus();
|
||||
|
||||
PowerMockito.mockStatic(DateUtil.class);
|
||||
when(DateUtil.now()).thenReturn(now);
|
||||
|
||||
GregorianCalendar calendar = new GregorianCalendar();
|
||||
calendar.setTimeInMillis(now);
|
||||
when(DateUtil.gregorianCalendar()).thenReturn(calendar);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue