Merge pull request #1701 from MilosKozak/automation

Automation
This commit is contained in:
Milos Kozak 2019-07-25 20:44:37 +02:00 committed by GitHub
commit c3be06d4e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
135 changed files with 9363 additions and 8 deletions

View file

@ -215,6 +215,7 @@ allprojects {
flatDir {
dirs 'libs'
}
maven { url 'https://jitpack.io' }
}
}
@ -274,6 +275,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.8.47"

View file

@ -19,6 +19,7 @@
<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<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="com.dexcom.cgm.EXTERNAL_PERMISSION" />
@ -161,6 +162,9 @@
<service
android:name=".services.DataService"
android:exported="false" />
<service
android:name=".services.LocationService"
android:exported="false" />
<service
android:name=".plugins.pump.danaR.services.DanaRExecutionService"
android:enabled="true"

View file

@ -45,6 +45,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;
@ -220,6 +221,8 @@ public class MainApp extends Application {
if (engineeringMode)
pluginsList.add(TidepoolPlugin.INSTANCE);
pluginsList.add(MaintenancePlugin.initPlugin(this));
if (engineeringMode)
pluginsList.add(AutomationPlugin.INSTANCE);
pluginsList.add(ConfigBuilderPlugin.getPlugin());
@ -466,4 +469,9 @@ public class MainApp extends Application {
}
}
public static int dpToPx(int dp) {
float scale = sResources.getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}

View file

@ -49,6 +49,7 @@ import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
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;
@ -190,6 +191,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResource(R.xml.pref_others);
addPreferencesFromResource(R.xml.pref_datachoices);

View file

@ -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;

View file

@ -9,6 +9,9 @@ import org.slf4j.LoggerFactory;
import java.util.Objects;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.interfaces.Interval;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.utils.DateUtil;
@ -191,4 +194,11 @@ public class TempTarget implements Interval {
'}';
}
public String friendlyDescription(String units) {
return Profile.toTargetRangeString(low, high, Constants.MGDL, units) +
units +
"@" + MainApp.gs(R.string.mins, durationInMinutes) +
(reason != null && !reason.equals("") ? "(" + reason + ")" : "");
}
}

View file

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

View file

@ -77,6 +77,7 @@ public class L {
public static final String CORE = "CORE";
public static final String AUTOSENS = "AUTOSENS";
public static final String AUTOMATION = "AUTOMATION";
public static final String EVENTS = "EVENTS";
public static final String GLUCOSE = "GLUCOSE";
public static final String BGSOURCE = "BGSOURCE";
@ -97,11 +98,13 @@ 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() {
logElements = new ArrayList<>();
logElements.add(new LogElement(APS, true));
logElements.add(new LogElement(AUTOMATION, true));
logElements.add(new LogElement(AUTOSENS, false));
logElements.add(new LogElement(BGSOURCE, true));
logElements.add(new LogElement(GLUCOSE, false));
@ -113,6 +116,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(TIDEPOOL, true));

View file

@ -0,0 +1,90 @@
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;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
public class AutomationEvent {
private Trigger trigger = new TriggerConnector();
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 TriggerConnector getPreconditions() {
TriggerConnector trigger = new TriggerConnector(TriggerConnector.Type.AND);
for (Action action : actions) {
if (action.precondition != null)
trigger.add(action.precondition);
}
return trigger;
}
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");
actions.clear();
for (int i = 0; i < array.length(); i++) {
actions.add(Action.instantiate(new JSONObject(array.getString(i))));
}
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
}

View file

@ -0,0 +1,73 @@
package info.nightscout.androidaps.plugins.general.automation
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.automation_fragment.*
class AutomationFragment : Fragment() {
private var disposable: CompositeDisposable = CompositeDisposable()
private var eventListAdapter: EventListAdapter? = null
operator fun CompositeDisposable.plusAssign(disposable: Disposable) {
add(disposable)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.automation_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
eventListAdapter = EventListAdapter(AutomationPlugin.automationEvents, fragmentManager)
automation_eventListView.layoutManager = LinearLayoutManager(context)
automation_eventListView.adapter = eventListAdapter
automation_fabAddEvent.setOnClickListener {
val dialog = EditEventDialog()
val args = Bundle()
args.putString("event", AutomationEvent().toJSON())
args.putInt("position", -1) // New event
dialog.arguments = args
fragmentManager?.let { dialog.show(it, "EditEventDialog") }
}
disposable += RxBus
.toObservable(EventAutomationUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
eventListAdapter?.notifyDataSetChanged()
val sb = StringBuilder()
for (l in AutomationPlugin.executionLog) {
sb.append(l)
sb.append("\n")
}
automation_logView.text = sb.toString()
}, {})
disposable += RxBus
.toObservable(EventAutomationDataChanged::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
eventListAdapter?.notifyDataSetChanged()
}, {})
}
override fun onStop() {
super.onStop()
disposable.clear()
}
}

View file

@ -0,0 +1,213 @@
package info.nightscout.androidaps.plugins.general.automation
import android.content.Intent
import android.os.Handler
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.logging.L
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.actions.*
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import info.nightscout.androidaps.plugins.general.automation.triggers.*
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.services.LocationService
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.SP
import info.nightscout.androidaps.utils.T
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.util.*
object AutomationPlugin : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(AutomationFragment::class.qualifiedName)
.pluginName(R.string.automation)
.shortName(R.string.automation_short)
.preferencesId(R.xml.pref_automation)
.description(R.string.automation_description)) {
private val log = LoggerFactory.getLogger(L.AUTOMATION)
private var disposable: CompositeDisposable = CompositeDisposable()
private const val key_AUTOMATION_EVENTS = "AUTOMATION_EVENTS"
val automationEvents = ArrayList<AutomationEvent>()
var executionLog: MutableList<String> = ArrayList()
private val loopHandler = Handler()
private lateinit var refreshLoop: Runnable
init {
refreshLoop = Runnable {
processActions()
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs())
}
}
operator fun CompositeDisposable.plusAssign(disposable: Disposable) {
add(disposable)
}
override fun onStart() {
val context = MainApp.instance().applicationContext
context.startService(Intent(context, LocationService::class.java))
super.onStart()
loadFromSP()
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs())
disposable += RxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ e ->
if (e.isChanged(R.string.key_location)) {
val ctx = MainApp.instance().applicationContext
ctx.stopService(Intent(ctx, LocationService::class.java))
ctx.startService(Intent(ctx, LocationService::class.java))
}
}, {})
disposable += RxBus
.toObservable(EventAutomationDataChanged::class.java)
.observeOn(Schedulers.io())
.subscribe({ storeToSP() }, {})
disposable += RxBus
.toObservable(EventLocationChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ e ->
e?.let {
log.debug("Grabbed location: $it.location.latitude $it.location.longitude Provider: $it.location.provider")
processActions()
}
}, {})
disposable += RxBus
.toObservable(EventChargingState::class.java)
.observeOn(Schedulers.io())
.subscribe({ processActions() }, {})
disposable += RxBus
.toObservable(EventNetworkChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ processActions() }, {})
disposable += RxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(Schedulers.io())
.subscribe({ processActions() }, {})
}
override fun onStop() {
disposable.clear()
loopHandler.removeCallbacks(refreshLoop)
val context = MainApp.instance().applicationContext
context.stopService(Intent(context, LocationService::class.java))
}
private fun storeToSP() {
val array = JSONArray()
try {
for (event in automationEvents) {
array.put(JSONObject(event.toJSON()))
}
} catch (e: JSONException) {
e.printStackTrace()
}
SP.putString(key_AUTOMATION_EVENTS, array.toString())
}
private fun loadFromSP() {
automationEvents.clear()
val data = SP.getString(key_AUTOMATION_EVENTS, "")
if (data != "") {
try {
val array = JSONArray(data)
for (i in 0 until array.length()) {
val o = array.getJSONObject(i)
val event = AutomationEvent().fromJSON(o.toString())
automationEvents.add(event)
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
}
@Synchronized
private fun processActions() {
if (!isEnabled(PluginType.GENERAL))
return
if (L.isEnabled(L.AUTOMATION))
log.debug("processActions")
for (event in automationEvents) {
if (event.trigger.shouldRun() && event.preconditions.shouldRun()) {
val actions = event.actions
for (action in actions) {
action.doAction(object : Callback() {
override fun run() {
val sb = StringBuilder()
sb.append(DateUtil.timeString(DateUtil.now()))
sb.append(" ")
sb.append(if (result.success) "" else "X")
sb.append(" ")
sb.append(event.title)
sb.append(": ")
sb.append(action.shortDescription())
sb.append(": ")
sb.append(result.comment)
executionLog.add(sb.toString())
if (L.isEnabled(L.AUTOMATION))
log.debug("Executed: $sb")
RxBus.send(EventAutomationUpdateGui())
}
})
}
event.trigger.executed(DateUtil.now())
}
}
storeToSP() // save last run time
}
fun getActionDummyObjects(): List<Action> {
return listOf(
//ActionLoopDisable(),
//ActionLoopEnable(),
//ActionLoopResume(),
//ActionLoopSuspend(),
ActionStartTempTarget(),
ActionStopTempTarget(),
ActionNotification(),
ActionProfileSwitchPercent()
)
}
fun getTriggerDummyObjects(): List<Trigger> {
return listOf(
TriggerTime(),
TriggerRecurringTime(),
TriggerBg(),
TriggerDelta(),
TriggerIob(),
TriggerCOB(),
TriggerProfilePercent(),
TriggerTempTarget(),
TriggerWifiSsid(),
TriggerLocation(),
TriggerAutosensValue(),
TriggerBolusAgo()
)
}
}

View file

@ -0,0 +1,120 @@
package info.nightscout.androidaps.plugins.general.automation;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
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.actions.Action;
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
private final List<AutomationEvent> mEventList;
private final FragmentManager mFragmentManager;
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);
}
// remove event
holder.iconTrash.setOnClickListener(v -> {
mEventList.remove(event);
notifyDataSetChanged();
});
// edit event
holder.rootLayout.setOnClickListener(v -> {
//EditEventDialog dialog = EditEventDialog.Companion.newInstance(event, false);
EditEventDialog dialog = new EditEventDialog();
Bundle args = new Bundle();
args.putString("event", event.toJSON());
args.putInt("position", position);
dialog.setArguments(args);
if (mFragmentManager != null)
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;
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);
}
}
}

View file

@ -0,0 +1,104 @@
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 javax.annotation.Nullable;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.queue.Callback;
/*
Action ideas:
* cancel temp target
* change preference setting
* enable/disable plugin
* create notification
* create ugly alarm
* create profile switch
* set/cancel tbr
* set/cancel extended bolus
* run bolus wizard
Trigger ideas:
* location (close to)
* connected to specific wifi
* internet available/not available
* nsclient connected/disconnected
* iob
* cob
* autosens value
* delta, short delta, long delta
* last bolus ago
* is tbr running
* bolus wizard result
* loop is enabled, disabled, suspended, running
*/
public abstract class Action {
public Trigger precondition = null;
public abstract int friendlyName();
public abstract String shortDescription();
public 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 Action fromJSON(String data) {
return this;
}
@Nullable
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;
}
public void apply(Action a) {
try {
JSONObject object = new JSONObject(a.toJSON());
String type = object.getString("type");
JSONObject data = object.getJSONObject("data");
if (type.equals(getClass().getName())) {
fromJSON(data.toString());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,49 @@
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.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.queue.Callback;
public class ActionLoopDisable extends Action {
@Override
public int friendlyName() {
return R.string.disableloop;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.disableloop);
}
@Override
public void doAction(Callback callback) {
if (LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, false);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopDisable");
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() {
@Override
public void run() {
RxBus.INSTANCE.send(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);
}
}

View file

@ -0,0 +1,44 @@
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.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.queue.Callback;
public class ActionLoopEnable extends Action {
@Override
public int friendlyName() {
return R.string.enableloop;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.enableloop);
}
@Override
public void doAction(Callback callback) {
if (!LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, true);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopEnable");
RxBus.INSTANCE.send(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);
}
}

View file

@ -0,0 +1,45 @@
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.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
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
public String shortDescription() {
return MainApp.gs(R.string.resumeloop);
}
@Override
public void doAction(Callback callback) {
if (LoopPlugin.getPlugin().isSuspended()) {
LoopPlugin.getPlugin().suspendTo(0);
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopResume");
NSUpload.uploadOpenAPSOffline(0);
RxBus.INSTANCE.send(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);
}
}

View file

@ -0,0 +1,91 @@
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.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.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionLoopSuspend extends Action {
public InputDuration minutes = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
@Override
public int friendlyName() {
return R.string.suspendloop;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.suspendloopforXmin, minutes.getMinutes());
}
@Override
public void doAction(Callback callback) {
if (!LoopPlugin.getPlugin().isSuspended()) {
LoopPlugin.getPlugin().suspendLoop(minutes.getMinutes());
RxBus.INSTANCE.send(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);
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
JSONObject data = new JSONObject();
try {
data.put("minutes", minutes.getMinutes());
o.put("type", this.getClass().getName());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject o = new JSONObject(data);
minutes.setMinutes(JsonHelper.safeGetInt(o, "minutes"));
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public boolean hasDialog() {
return true;
}
@Override
public void generateDialog(LinearLayout root) {
new LayoutBuilder()
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", minutes))
.build(root);
}
}

View file

@ -0,0 +1,91 @@
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.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.automation.elements.InputString;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionNotification extends Action {
public InputString text = new InputString();
@Override
public int friendlyName() {
return R.string.notification;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.notification_message, text.getValue());
}
@Override
public void doAction(Callback callback) {
Notification notification = new Notification(Notification.USERMESSAGE, text.getValue(), Notification.URGENT);
RxBus.INSTANCE.send(new EventNewNotification(notification));
NSUpload.uploadError(text.getValue());
RxBus.INSTANCE.send(new EventRefreshOverview("ActionNotification"));
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_notifications);
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
JSONObject data = new JSONObject();
try {
data.put("text", text.getValue());
o.put("type", this.getClass().getName());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject o = new JSONObject(data);
text.setValue(JsonHelper.safeGetString(o, "text"));
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public boolean hasDialog() {
return true;
}
@Override
public void generateDialog(LinearLayout root) {
new LayoutBuilder()
.add(new LabelWithElement(MainApp.gs(R.string.message_short), "", text))
.build(root);
}
}

View file

@ -0,0 +1,95 @@
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.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
import info.nightscout.androidaps.plugins.general.automation.elements.InputPercent;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerProfilePercent;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionProfileSwitchPercent extends Action {
InputPercent pct = new InputPercent();
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
public ActionProfileSwitchPercent() {
precondition = new TriggerProfilePercent().comparator(Comparator.Compare.IS_EQUAL).setValue(100);
}
@Override
public int friendlyName() {
return R.string.profilepercentage;
}
@Override
public String shortDescription() {
if (duration.getMinutes() == 0)
return MainApp.gs(R.string.startprofileforever, (int) pct.getValue());
else
return MainApp.gs(R.string.startprofile, (int) pct.getValue(), duration.getMinutes());
}
@Override
public void doAction(Callback callback) {
ProfileFunctions.doProfileSwitch((int) duration.getValue(), (int) pct.getValue(), 0);
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
}
@Override
public void generateDialog(LinearLayout root) {
new LayoutBuilder()
.add(new LabelWithElement(MainApp.gs(R.string.percent_u), "", pct))
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", duration))
.build(root);
}
@Override
public boolean hasDialog() {
return true;
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", ActionProfileSwitchPercent.class.getName());
JSONObject data = new JSONObject();
data.put("percentage", pct.getValue());
data.put("durationInMinutes", duration.getMinutes());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
pct.setValue(JsonHelper.safeGetInt(d, "percentage"));
duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes"));
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_actions_profileswitch);
}
}

View file

@ -0,0 +1,119 @@
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.Profile;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.plugins.general.automation.elements.ComparatorExists;
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.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTempTarget;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionStartTempTarget extends Action {
String reason = "";
InputBg value = new InputBg();
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
private TempTarget tempTarget;
public ActionStartTempTarget() {
precondition = new TriggerTempTarget().comparator(ComparatorExists.Compare.NOT_EXISTS);
}
@Override
public int friendlyName() {
return R.string.starttemptarget;
}
@Override
public String shortDescription() {
tempTarget = new TempTarget()
.date(DateUtil.now())
.duration(duration.getMinutes())
.reason(reason)
.source(Source.USER)
.low(Profile.toMgdl(value.getValue(), value.getUnits()))
.high(Profile.toMgdl(value.getValue(), value.getUnits()));
return MainApp.gs(R.string.starttemptarget) + ": " + (tempTarget == null ? "null" : tempTarget.friendlyDescription(value.getUnits()));
}
@Override
public void doAction(Callback callback) {
tempTarget = new TempTarget()
.date(DateUtil.now())
.duration(duration.getMinutes())
.reason(reason)
.source(Source.USER)
.low(Profile.toMgdl(value.getValue(), value.getUnits()))
.high(Profile.toMgdl(value.getValue(), value.getUnits()));
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;
new LayoutBuilder()
.add(new LabelWithElement(MainApp.gs(R.string.careportal_temporarytarget), MainApp.gs(unitResId), value))
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", duration))
.build(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("value", value.getValue());
data.put("units", value.getUnits());
data.put("durationInMinutes", duration.getMinutes());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
reason = JsonHelper.safeGetString(d, "reason");
value.setUnits(JsonHelper.safeGetString(d, "units"));
value.setValue(JsonHelper.safeGetDouble(d, "value"));
duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes"));
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_cp_cgm_target);
}
}

View file

@ -0,0 +1,77 @@
package info.nightscout.androidaps.plugins.general.automation.actions;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
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.queue.Callback;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionStopTempTarget extends Action {
String reason = "";
private TempTarget tempTarget;
public ActionStopTempTarget() {
}
@Override
public int friendlyName() {
return R.string.stoptemptarget;
}
@Override
public String shortDescription() {
return MainApp.gs(R.string.stoptemptarget);
}
@Override
public void doAction(Callback callback) {
tempTarget = new TempTarget().date(DateUtil.now()).duration(0).reason(reason).source(Source.USER).low(0).high(0);
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
if (callback != null)
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
}
@Override
public boolean hasDialog() {
return false;
}
@Override
public String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", ActionStopTempTarget.class.getName());
JSONObject data = new JSONObject();
data.put("reason", reason);
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
public Action fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
reason = JsonHelper.safeGetString(d, "reason");
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_stop_24dp);
}
}

View file

@ -0,0 +1,54 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.actions.Action
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
class ActionListAdapter(private val fragmentManager: FragmentManager, private val actionList: MutableList<Action>) : RecyclerView.Adapter<ActionListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.automation_action_item, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val action = actionList[position]
holder.bind(action, fragmentManager, this, position, actionList)
}
override fun getItemCount(): Int {
return actionList.size
}
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(action: Action, fragmentManager: FragmentManager, recyclerView: RecyclerView.Adapter<ViewHolder>, position : Int, actionList: MutableList<Action>) {
view.findViewById<LinearLayout>(R.id.automation_layoutText).setOnClickListener {
if (action.hasDialog()) {
val args = Bundle()
args.putInt("actionPosition", position)
args.putString("action", action.toJSON())
val dialog = EditActionDialog()
dialog.arguments = args
dialog.show(fragmentManager, "EditActionDialog")
}
}
view.findViewById<ImageView>(R.id.automation_iconTrash).setOnClickListener {
actionList.remove(action)
recyclerView.notifyDataSetChanged()
RxBus.send(EventAutomationUpdateGui())
}
view.findViewById<TextView>(R.id.automation_viewActionTitle).text = action.shortDescription()
}
}
}

View file

@ -0,0 +1,85 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RadioButton
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.automation.actions.Action
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationAddAction
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import kotlinx.android.synthetic.main.automation_dialog_choose_action.*
import kotlinx.android.synthetic.main.okcancel.*
class ChooseActionDialog : DialogFragment() {
var checkedIndex = -1
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// restore checked radio button
savedInstanceState?.let { bundle ->
checkedIndex = bundle.getInt("checkedIndex")
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_choose_action, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
for (a in AutomationPlugin.getActionDummyObjects()) {
val radioButton = RadioButton(context)
radioButton.setText(a.friendlyName())
radioButton.tag = a.javaClass
automation_radioGroup.addView(radioButton)
}
if (checkedIndex != -1)
(automation_radioGroup.getChildAt(checkedIndex) as RadioButton).isChecked = true
// OK button
ok.setOnClickListener {
dismiss()
instantiateAction()?.let {
RxBus.send(EventAutomationAddAction(it))
RxBus.send(EventAutomationUpdateGui())
}
}
// Cancel button
cancel.setOnClickListener { dismiss() }
}
override fun onSaveInstanceState(bundle: Bundle) {
bundle.putInt("checkedIndex", determineCheckedIndex())
}
private fun instantiateAction(): Action? {
return getActionClass()?.let {
it.newInstance() as Action
}
}
private fun getActionClass(): Class<*>? {
val radioButtonID = automation_radioGroup.checkedRadioButtonId
val radioButton = automation_radioGroup.findViewById<RadioButton>(radioButtonID)
return radioButton?.let {
it.tag as Class<*>
}
}
private fun determineCheckedIndex(): Int {
for (i in 0 until automation_radioGroup.childCount) {
if ((automation_radioGroup.getChildAt(i) as RadioButton).isChecked)
return i
}
return -1
}
}

View file

@ -0,0 +1,89 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RadioButton
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import kotlinx.android.synthetic.main.automation_dialog_choose_trigger.*
import kotlinx.android.synthetic.main.okcancel.*
class ChooseTriggerDialog : DialogFragment() {
private var checkedIndex = -1
private var clickListener: OnClickListener? = null
interface OnClickListener {
fun onClick(newTriggerObject: Trigger?)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// restore checked radio button
savedInstanceState?.let { bundle ->
checkedIndex = bundle.getInt("checkedIndex")
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_choose_trigger, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
for (t in AutomationPlugin.getTriggerDummyObjects()) {
val radioButton = RadioButton(context)
radioButton.setText(t.friendlyName())
radioButton.tag = t.javaClass
automation_chooseTriggerRadioGroup.addView(radioButton)
}
if (checkedIndex != -1)
(automation_chooseTriggerRadioGroup.getChildAt(checkedIndex) as RadioButton).isChecked = true
// OK button
ok.setOnClickListener {
dismiss()
clickListener?.onClick(instantiateTrigger())
}
// Cancel button
cancel.setOnClickListener { dismiss() }
}
fun setOnClickListener(clickListener: OnClickListener) {
this.clickListener = clickListener
}
override fun onSaveInstanceState(bundle: Bundle) {
bundle.putInt("checkedIndex", determineCheckedIndex())
}
private fun instantiateTrigger(): Trigger? {
return getTriggerClass()?.let {
it.newInstance() as Trigger
}
}
private fun getTriggerClass(): Class<*>? {
val radioButtonID = automation_chooseTriggerRadioGroup.checkedRadioButtonId
val radioButton = automation_chooseTriggerRadioGroup.findViewById<RadioButton>(radioButtonID)
return radioButton?.let {
it.tag as Class<*>
}
}
private fun determineCheckedIndex(): Int {
for (i in 0 until automation_chooseTriggerRadioGroup.childCount) {
if ((automation_chooseTriggerRadioGroup.getChildAt(i) as RadioButton).isChecked)
return i
}
return -1
}
}

View file

@ -0,0 +1,62 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.actions.Action
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateAction
import kotlinx.android.synthetic.main.automation_dialog_action.*
import kotlinx.android.synthetic.main.okcancel.*
import org.json.JSONObject
class EditActionDialog : DialogFragment() {
private var action: Action? = null
private var actionPosition: Int = -1
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
actionPosition = bundle.getInt("actionPosition", -1)
bundle.getString("action")?.let { action = Action.instantiate(JSONObject(it)) }
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_action, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
action?.let {
automation_actionTitle.setText(it.friendlyName())
automation_editActionLayout.removeAllViews()
it.generateDialog(automation_editActionLayout)
}
// OK button
ok.setOnClickListener {
dismiss()
action?.let {
RxBus.send(EventAutomationUpdateAction(it, actionPosition))
}
}
// Cancel button
cancel.setOnClickListener { dismiss() }
}
override fun onSaveInstanceState(bundle: Bundle) {
super.onSaveInstanceState(bundle)
action?.let {
bundle.putInt("actionPosition", actionPosition)
bundle.putString("action", it.toJSON())
}
}
}

View file

@ -0,0 +1,154 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.AutomationEvent
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.automation.events.*
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.utils.ToastUtils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.automation_dialog_event.*
import kotlinx.android.synthetic.main.okcancel.*
class EditEventDialog : DialogFragment() {
private var actionListAdapter: ActionListAdapter? = null
private var event: AutomationEvent = AutomationEvent()
private var position: Int = -1
private var disposable: CompositeDisposable = CompositeDisposable()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
position = bundle.getInt("position", -1)
bundle.getString("event")?.let { event = AutomationEvent().fromJSON(it) }
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_event, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
automation_inputEventTitle.setText(event.title)
automation_triggerDescription.text = event.trigger.friendlyDescription()
automation_editTrigger.setOnClickListener {
val args = Bundle()
args.putString("trigger", event.trigger.toJSON())
val dialog = EditTriggerDialog()
dialog.arguments = args
fragmentManager?.let { dialog.show(it, "EditTriggerDialog") }
}
// setup action list view
fragmentManager?.let { actionListAdapter = ActionListAdapter(it, event.actions) }
automation_actionListView.layoutManager = LinearLayoutManager(context)
automation_actionListView.adapter = actionListAdapter
automation_addAction.setOnClickListener { fragmentManager?.let { ChooseActionDialog().show(it, "ChooseActionDialog") } }
// OK button
ok.setOnClickListener {
// check for title
val title = automation_inputEventTitle.text.toString()
if (title.isEmpty()) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_task_name)
return@setOnClickListener
}
event.title = title
// check for at least one trigger
val con = event.trigger as TriggerConnector
if (con.size() == 0) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_trigger)
return@setOnClickListener
}
// check for at least one action
if (event.actions.isEmpty()) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_action)
return@setOnClickListener
}
// store
if (position == -1)
AutomationPlugin.automationEvents.add(event)
else
AutomationPlugin.automationEvents[position] = event
dismiss()
RxBus.send(EventAutomationDataChanged())
}
// Cancel button
cancel.setOnClickListener { dismiss() }
showPreconditions()
disposable.add(RxBus
.toObservable(EventAutomationUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
actionListAdapter?.notifyDataSetChanged()
showPreconditions()
}, {})
)
disposable.add(RxBus
.toObservable(EventAutomationAddAction::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.addAction(it.action)
actionListAdapter?.notifyDataSetChanged()
}, {})
)
disposable.add(RxBus
.toObservable(EventAutomationUpdateTrigger::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.trigger = it.trigger
automation_triggerDescription.text = event.trigger.friendlyDescription()
}, {})
)
disposable.add(RxBus
.toObservable(EventAutomationUpdateAction::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.actions[it.position] = it.action
actionListAdapter?.notifyDataSetChanged()
}, {})
)
}
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
}
override fun onSaveInstanceState(bundle: Bundle) {
super.onSaveInstanceState(bundle)
bundle.putString("event", event.toJSON())
bundle.putInt("position", position)
}
private fun showPreconditions() {
val forcedTriggers = event.preconditions
if (forcedTriggers.size() > 0) {
automation_forcedTriggerDescription.visibility = View.VISIBLE
automation_forcedTriggerDescriptionLabel.visibility = View.VISIBLE
automation_forcedTriggerDescription.text = forcedTriggers.friendlyDescription()
} else {
automation_forcedTriggerDescription.visibility = View.GONE
automation_forcedTriggerDescriptionLabel.visibility = View.GONE
}
}
}

View file

@ -0,0 +1,50 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateTrigger
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import kotlinx.android.synthetic.main.automation_dialog_edit_trigger.*
import kotlinx.android.synthetic.main.okcancel.*
class EditTriggerDialog : DialogFragment() {
private var trigger: Trigger? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
bundle.getString("trigger")?.let { trigger = Trigger.instantiate(it) }
}
dialog.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.automation_dialog_edit_trigger, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// display root trigger
trigger?.let { it.generateDialog(automation_layoutTrigger, fragmentManager) }
// OK button
ok.setOnClickListener {
dismiss()
RxBus.send(EventAutomationUpdateTrigger(trigger!!))
}
// Cancel button
cancel.setOnClickListener { dismiss() }
}
override fun onSaveInstanceState(bundle: Bundle) {
super.onSaveInstanceState(bundle)
trigger?.let { bundle.putString("trigger", it.toJSON()) }
}
}

View file

@ -0,0 +1,186 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.fragment.app.FragmentManager;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
public class TriggerListAdapter {
private final LinearLayout mRootLayout;
private final FragmentManager mFragmentManager;
private final Context mContext;
private final TriggerConnector mRootConnector;
public TriggerListAdapter(FragmentManager fragmentManager, Context context, LinearLayout rootLayout, TriggerConnector rootTrigger) {
mRootLayout = rootLayout;
mFragmentManager = fragmentManager;
mContext = context;
mRootConnector = rootTrigger;
build(fragmentManager);
}
public Context getContext() {
return mContext;
}
private FragmentManager getFM() {
return mFragmentManager;
}
private void destroy() {
mRootLayout.removeAllViews();
}
private void build(FragmentManager fragmentManager) {
for (int i = 0; i < mRootConnector.size(); ++i) {
final Trigger trigger = mRootConnector.get(i);
// spinner
if (i > 0) {
createSpinner(trigger);
}
// trigger layout
trigger.generateDialog(mRootLayout, fragmentManager);
// buttons
createButtons(fragmentManager, trigger);
}
if (mRootConnector.size() == 0) {
Button buttonAdd = new Button(mContext);
buttonAdd.setText(MainApp.gs(R.string.addnew));
buttonAdd.setOnClickListener(v -> {
ChooseTriggerDialog dialog = new ChooseTriggerDialog();
dialog.setOnClickListener(newTriggerObject -> {
mRootConnector.add(newTriggerObject);
rebuild(fragmentManager);
});
dialog.show(fragmentManager, "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) {
// connector type changed
changeConnector(getFM(), trigger, connector, TriggerConnector.Type.values()[position]);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
mRootLayout.addView(spinner);
}
private void createButtons(FragmentManager fragmentManager, 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(MainApp.gs(R.string.delete_short));
buttonRemove.setOnClickListener(v -> {
final TriggerConnector connector = trigger.getConnector();
connector.remove(trigger);
connector.simplify().rebuildView(getFM());
});
buttonLayout.addView(buttonRemove);
// Button [+]
Button buttonAdd = new Button(mContext);
buttonAdd.setText(MainApp.gs(R.string.add_short));
buttonAdd.setOnClickListener(v -> {
ChooseTriggerDialog dialog = new ChooseTriggerDialog();
dialog.show(fragmentManager, "ChooseTriggerDialog");
dialog.setOnClickListener(newTriggerObject -> {
TriggerConnector connector = trigger.getConnector();
connector.add(connector.pos(trigger) + 1, newTriggerObject);
connector.simplify().rebuildView(getFM());
});
});
buttonLayout.addView(buttonAdd);
// Button [*]
Button buttonCopy = new Button(mContext);
buttonCopy.setText(MainApp.gs(R.string.copy_short));
buttonCopy.setOnClickListener(v -> {
TriggerConnector connector = trigger.getConnector();
connector.add(connector.pos(trigger) + 1, trigger.duplicate());
connector.simplify().rebuildView(getFM());
});
buttonLayout.addView(buttonCopy);
}
public void rebuild(FragmentManager fragmentManager) {
destroy();
build(fragmentManager);
}
public static void changeConnector(final FragmentManager fragmentManager, 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(fragmentManager);
}
}

View file

@ -0,0 +1,123 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.annotation.StringRes;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class Comparator extends Element {
public enum Compare {
IS_LESSER,
IS_EQUAL_OR_LESSER,
IS_EQUAL,
IS_EQUAL_OR_GREATER,
IS_GREATER,
IS_NOT_AVAILABLE;
public @StringRes
int getStringRes() {
switch (this) {
case IS_LESSER:
return R.string.islesser;
case IS_EQUAL_OR_LESSER:
return R.string.isequalorlesser;
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(IS_NOT_AVAILABLE);
int comparison = obj1.compareTo(obj2);
switch (this) {
case IS_LESSER:
return comparison < 0;
case IS_EQUAL_OR_LESSER:
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 (Compare c : Compare.values()) {
list.add(MainApp.gs(c.getStringRes()));
}
return list;
}
}
private Compare compare = Compare.IS_EQUAL;
public Comparator() {
super();
}
public Comparator(Comparator another) {
super();
compare = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
Spinner spinner = new Spinner(root.getContext());
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, Compare.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) {
compare = Compare.values()[position];
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
spinner.setSelection(compare.ordinal());
root.addView(spinner);
}
public Compare getValue() {
return compare;
}
public Comparator setValue(Compare compare) {
this.compare = compare;
return this;
}
}

View file

@ -0,0 +1,90 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.annotation.StringRes;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class ComparatorExists extends Element {
public enum Compare {
EXISTS,
NOT_EXISTS;
public @StringRes
int getStringRes() {
switch (this) {
case EXISTS:
return R.string.exists;
case NOT_EXISTS:
return R.string.notexists;
default:
return R.string.unknown;
}
}
public static List<String> labels() {
List<String> list = new ArrayList<>();
for (Compare c : Compare.values()) {
list.add(MainApp.gs(c.getStringRes()));
}
return list;
}
}
private Compare compare = Compare.EXISTS;
public ComparatorExists() {
super();
}
public ComparatorExists(ComparatorExists another) {
super();
compare = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
Spinner spinner = new Spinner(root.getContext());
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, Compare.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) {
compare = Compare.values()[position];
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
spinner.setSelection(compare.ordinal());
root.addView(spinner);
}
public Compare getValue() {
return compare;
}
public ComparatorExists setValue(Compare compare) {
this.compare = compare;
return this;
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.widget.LinearLayout;
public abstract class Element {
public abstract void addToLayout(LinearLayout root);
}

View file

@ -0,0 +1,99 @@
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.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.utils.NumberPicker;
public class InputBg extends Element {
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if (units.equals(Constants.MMOL)) {
value = Math.max(value, 4d);
value = Math.min(value, 15d);
} else {
value = Math.max(value, 72d);
value = Math.min(value, 270d);
}
}
@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 = Constants.MGDL;
private double value;
double minValue;
private double maxValue;
private double step;
private DecimalFormat decimalFormat;
public InputBg() {
super();
setUnits(ProfileFunctions.getInstance().getProfileUnits());
}
public InputBg(InputBg another) {
super();
value = another.getValue();
setUnits(another.getUnits());
}
@Override
public void addToLayout(LinearLayout root) {
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
numberPicker.setOnValueChangedListener(value -> this.value = value);
root.addView(numberPicker);
}
public String getUnits() {
return units;
}
public InputBg setUnits(String units) {
// set default initial value
if (units.equals(Constants.MMOL)) {
// mmol
minValue = 2;
maxValue = 30;
step = 0.1;
decimalFormat = new DecimalFormat("0.0");
} else {
// mg/dL
minValue = 40;
maxValue = 540;
step = 1;
decimalFormat = new DecimalFormat("0");
}
// make sure that value is in range
textWatcher.afterTextChanged(null);
this.units = units;
return this;
}
public InputBg setValue(double value) {
this.value = value;
return this;
}
public double getValue() {
return value;
}
}

View file

@ -0,0 +1,28 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.widget.Button;
import android.widget.LinearLayout;
public class InputButton extends Element {
String text;
Runnable runnable;
public InputButton(String text, Runnable runnable) {
this.text = text;
this.runnable = runnable;
}
@Override
public void addToLayout(LinearLayout root) {
Button button = new Button(root.getContext());
button.setText(text);
button.setOnClickListener(view -> {
if (runnable != null)
runnable.run();
});
root.addView(button);
}
}

View file

@ -0,0 +1,151 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.text.Editable;
import android.text.TextWatcher;
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 androidx.annotation.StringRes;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.NumberPicker;
public class InputDelta extends Element {
private Comparator.Compare compare = Comparator.Compare.IS_EQUAL;
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
}
@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 enum DeltaType {
DELTA,
SHORT_AVERAGE,
LONG_AVERAGE;
public @StringRes
int getStringRes() {
switch (this) {
case DELTA:
return R.string.delta;
case SHORT_AVERAGE:
return R.string.short_avgdelta;
case LONG_AVERAGE:
return R.string.long_avgdelta;
default:
return R.string.unknown;
}
}
public static List<String> labels() {
List<String> list = new ArrayList<>();
for (DeltaType d : DeltaType.values()) {
list.add(MainApp.gs(d.getStringRes()));
}
return list;
}
}
private double value;
double minValue;
double maxValue;
private double step;
private DecimalFormat decimalFormat;
private DeltaType deltaType;
NumberPicker numberPicker;
public InputDelta() {
super();
}
public InputDelta(double value, double minValue, double maxValue, double step, DecimalFormat decimalFormat, DeltaType deltaType) {
super();
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.step = step;
this.decimalFormat = decimalFormat;
this.deltaType = deltaType;
}
public InputDelta(InputDelta another) {
super();
value = another.getValue();
minValue = another.minValue;
maxValue = another.maxValue;
step = another.step;
decimalFormat = another.decimalFormat;
deltaType = another.deltaType;
}
@Override
public void addToLayout(LinearLayout root) {
Spinner spinner = new Spinner(root.getContext());
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, DeltaType.labels());
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerArrayAdapter);
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
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) {
deltaType = DeltaType.values()[position];
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
spinner.setSelection(this.deltaType.ordinal());
// root.addView(spinner);
numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
numberPicker.setOnValueChangedListener(value -> this.value = value);
LinearLayout l = new LinearLayout(root.getContext());
l.setOrientation(LinearLayout.VERTICAL);
l.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
l.addView(spinner);
l.addView(numberPicker);
root.addView(l);
}
public InputDelta setValue(double value, DeltaType type) {
this.value = value;
this.deltaType = type;
if (numberPicker != null)
numberPicker.setValue(value);
return this;
}
public double getValue() {
return value;
}
public DeltaType getDeltaType() {
return deltaType;
}
}

View file

@ -0,0 +1,77 @@
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.utils.NumberPicker;
public class InputDouble extends Element {
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
}
@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 double value;
double minValue;
double maxValue;
private double step;
private DecimalFormat decimalFormat;
NumberPicker numberPicker;
public InputDouble() {
super();
}
public InputDouble(double value, double minValue, double maxValue, double step, DecimalFormat decimalFormat) {
super();
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.step = step;
this.decimalFormat = decimalFormat;
}
public InputDouble(InputDouble another) {
super();
value = another.getValue();
minValue = another.minValue;
maxValue = another.maxValue;
step = another.step;
decimalFormat = another.decimalFormat;
}
@Override
public void addToLayout(LinearLayout root) {
numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
numberPicker.setOnValueChangedListener(value -> this.value = value);
root.addView(numberPicker);
}
public InputDouble setValue(double value) {
this.value = value;
if (numberPicker != null)
numberPicker.setValue(value);
return this;
}
public double getValue() {
return value;
}
}

View file

@ -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 int value;
public InputDuration(int value, TimeUnit unit) {
this.unit = unit;
this.value = value;
}
@Override
public void addToLayout(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, null);
} else {
// Hours
numberPicker.setParams(0d, 0d, 24d, 1d, new DecimalFormat("0"), false, null);
}
numberPicker.setValue((double) value);
numberPicker.setOnValueChangedListener(value -> this.value = (int) value);
root.addView(numberPicker);
}
TimeUnit getUnit() {
return unit;
}
public double getValue() {
return value;
}
public void setMinutes(int value) {
if (unit.equals(TimeUnit.MINUTES)) {
this.value = value;
} else {
this.value = value / 60;
}
}
public int getMinutes() {
if (unit.equals(TimeUnit.MINUTES)) {
return value;
} else {
return value * 60;
}
}
}

View file

@ -0,0 +1,57 @@
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.utils.NumberPicker;
public class InputInsulin extends Element {
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
value = Math.max(value, -20d);
value = Math.min(value, 20d);
}
@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 double value;
public InputInsulin() {
super();
}
public InputInsulin(InputInsulin another) {
super();
value = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(0d, -20d, 20d, 0.1, new DecimalFormat("0.0"), true, null, textWatcher);
numberPicker.setValue(value);
numberPicker.setOnValueChangedListener(value -> this.value = value);
root.addView(numberPicker);
}
public double getValue() {
return value;
}
public InputInsulin setValue(double value) {
this.value = value;
return this;
}
}

View file

@ -0,0 +1,58 @@
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.utils.NumberPicker;
public class InputPercent extends Element {
final TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
value = Math.max(value, 70d);
value = Math.min(value, 130d);
}
@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 double value;
public InputPercent() {
super();
value = 100d;
}
public InputPercent(InputPercent another) {
super();
value = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
numberPicker.setParams(100d, 70d, 130d, 5d, new DecimalFormat("0"), true, null, textWatcher);
numberPicker.setValue(value);
numberPicker.setOnValueChangedListener(value -> this.value = value);
root.addView(numberPicker);
}
public double getValue() {
return value;
}
public InputPercent setValue(double value) {
this.value = value;
return this;
}
}

View file

@ -0,0 +1,56 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
public class InputString extends Element {
TextWatcher textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
value = s.toString();
}
};
private String value = "";
public InputString() {
super();
}
public InputString(InputString another) {
super();
value = another.getValue();
}
@Override
public void addToLayout(LinearLayout root) {
EditText editText = new EditText(root.getContext());
editText.setText(value);
editText.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
editText.addTextChangedListener(textWatcher);
root.addView(editText);
}
public InputString setValue(String value) {
this.value = value;
return this;
}
public String getValue() {
return value;
}
}

View file

@ -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 LabelWithElement extends Element {
final Element element;
final String textPre;
final String textPost;
public LabelWithElement(String textPre, String textPost, Element element) {
this.element = element;
this.textPre = textPre;
this.textPost = textPost;
}
@Override
public void addToLayout(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.addToLayout(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);
}
}

View file

@ -0,0 +1,26 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.widget.LinearLayout;
import java.util.ArrayList;
public class LayoutBuilder {
ArrayList<Element> mElements = new ArrayList<>();
public LayoutBuilder add(Element element) {
mElements.add(element);
return this;
}
public LayoutBuilder add(Element element, boolean condition) {
if (condition) mElements.add(element);
return this;
}
public void build(LinearLayout layout) {
for (Element e : mElements) {
e.addToLayout(layout);
}
}
}

View file

@ -0,0 +1,36 @@
package info.nightscout.androidaps.plugins.general.automation.elements;
import android.graphics.Typeface;
import android.widget.LinearLayout;
import android.widget.TextView;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class StaticLabel extends Element {
String label;
public StaticLabel(String label) {
super();
this.label = label;
}
public StaticLabel(int resourceId) {
super();
this.label = MainApp.gs(resourceId);
}
@Override
public void addToLayout(LinearLayout root) {
// text view pre element
int px = MainApp.dpToPx(10);
TextView textView = new TextView(root.getContext());
textView.setText(label);
// textViewPre.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
textView.setPadding(px, px, px, px);
textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
textView.setBackgroundColor(MainApp.gc(R.color.mdtp_line_dark));
// add element to layout
root.addView(textView);
}
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.general.automation.actions.Action
class EventAutomationAddAction(val action: Action) : Event()

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event
class EventAutomationDataChanged : Event()

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.general.automation.actions.Action
class EventAutomationUpdateAction(val action: Action, val position : Int) : Event() {
}

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event
class EventAutomationUpdateGui : Event()

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
class EventAutomationUpdateTrigger(val trigger: Trigger) : Event()

View file

@ -0,0 +1,94 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import javax.annotation.Nullable;
public abstract class Trigger {
TriggerConnector connector = null;
long lastRun;
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();
public void executed(long time) {
lastRun = time;
}
public long getLastRun() {
return lastRun;
}
public abstract Trigger duplicate();
public static Trigger instantiate(String json) {
try {
return instantiate(new JSONObject(json));
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
@Nullable
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 void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
TextView title = new TextView(root.getContext());
title.setText(friendlyName());
root.addView(title);
}
@Nullable
Activity scanForActivity(Context cont) {
if (cont == null)
return null;
else if (cont instanceof Activity)
return (Activity) cont;
else if (cont instanceof ContextWrapper)
return scanForActivity(((ContextWrapper) cont).getBaseContext());
return null;
}
}

View file

@ -0,0 +1,153 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.T;
public class TriggerAutosensValue extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private final int minValue = (int) (SP.getDouble("openapsama_autosens_min", 0.7d) * 100);
private final int maxValue = (int) (SP.getDouble("openapsama_autosens_max", 1.2d) * 100);
private final double step = 1;
private DecimalFormat decimalFormat = new DecimalFormat("1");
private InputDouble value = new InputDouble(100, (double) minValue, (double) maxValue, step, decimalFormat);
private Comparator comparator = new Comparator();
public TriggerAutosensValue() {
super();
}
private TriggerAutosensValue(TriggerAutosensValue triggerAutosensValue) {
super();
value = new InputDouble(triggerAutosensValue.value);
lastRun = triggerAutosensValue.lastRun;
comparator = new Comparator(triggerAutosensValue.comparator);
}
public double getValue() {
return value.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getLastAutosensData("Automation trigger");
if (autosensData == null)
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
return true;
else
return false;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
boolean doRun = comparator.getValue().check((autosensData.autosensResult.ratio), getValue() / 100d);
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerAutosensValue.class.getName());
JSONObject data = new JSONObject();
data.put("value", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
value.setValue(JsonHelper.safeGetDouble(d, "value"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.autosenslabel;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.autosenscompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.as);
}
@Override
public Trigger duplicate() {
return new TriggerAutosensValue(this);
}
TriggerAutosensValue setValue(int requestedValue) {
this.value.setValue(requestedValue);
return this;
}
TriggerAutosensValue lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerAutosensValue comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.autosenslabel))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.autosenslabel) + ": ", "", value))
.build(root);
}
}

View file

@ -0,0 +1,169 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputBg;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerBg extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private InputBg bg = new InputBg();
private Comparator comparator = new Comparator();
public TriggerBg() {
super();
}
private TriggerBg(TriggerBg triggerBg) {
super();
bg = new InputBg(triggerBg.bg);
comparator = new Comparator(triggerBg.comparator);
lastRun = triggerBg.lastRun;
}
public double getValue() {
return bg.getValue();
}
public Comparator getComparator() {
return comparator;
}
public String getUnits() {
return bg.getUnits();
}
public long getLastRun() {
return lastRun;
}
@Override
public synchronized boolean shouldRun() {
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
if (glucoseStatus == null && comparator.getValue().equals(Comparator.Compare.IS_NOT_AVAILABLE)) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
if (glucoseStatus == null)
return false;
boolean doRun = comparator.getValue().check(glucoseStatus.glucose, Profile.toMgdl(bg.getValue(), bg.getUnits()));
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerBg.class.getName());
JSONObject data = new JSONObject();
data.put("bg", bg.getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
data.put("units", bg.getUnits());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
bg.setUnits(JsonHelper.safeGetString(d, "units"));
bg.setValue(JsonHelper.safeGetDouble(d, "bg"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.glucose;
}
@Override
public String friendlyDescription() {
if (comparator.getValue().equals(Comparator.Compare.IS_NOT_AVAILABLE))
return MainApp.gs(R.string.glucoseisnotavailable);
else {
return MainApp.gs(bg.getUnits().equals(Constants.MGDL) ? R.string.glucosecomparedmgdl : R.string.glucosecomparedmmol, MainApp.gs(comparator.getValue().getStringRes()), bg.getValue(), bg.getUnits());
}
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_cp_bgcheck);
}
@Override
public Trigger duplicate() {
return new TriggerBg(this);
}
TriggerBg setValue(double value) {
bg.setValue(value);
return this;
}
TriggerBg lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerBg comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
TriggerBg setUnits(String units) {
bg.setUnits(units);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.glucose))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.glucose_u, bg.getUnits()), "", bg))
.build(root);
}
}

View file

@ -0,0 +1,154 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerBolusAgo extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private final double step = 1;
private DecimalFormat decimalFormat = new DecimalFormat("1");
public InputDuration minutesAgo = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
private Comparator comparator = new Comparator();
public TriggerBolusAgo() {
super();
}
private TriggerBolusAgo(TriggerBolusAgo triggerBolusAgo) {
super();
minutesAgo = new InputDuration(triggerBolusAgo.minutesAgo.getMinutes(), InputDuration.TimeUnit.MINUTES);
lastRun = triggerBolusAgo.lastRun;
comparator = new Comparator(triggerBolusAgo.comparator);
}
public double getValue() {
return minutesAgo.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
long lastBolusTime = TreatmentsPlugin.getPlugin().getLastBolusTime(false);
if (lastBolusTime == 0)
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
return true;
else
return false;
double minutesAgo = (double) (DateUtil.now() - lastBolusTime) / (60 * 1000);
if (L.isEnabled(L.AUTOMATION))
log.debug("LastBolus min ago: " + minutesAgo);
boolean doRun = comparator.getValue().check((minutesAgo), getValue());
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerBolusAgo.class.getName());
JSONObject data = new JSONObject();
data.put("minutesAgo", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
minutesAgo.setMinutes(JsonHelper.safeGetInt(d, "minutesAgo"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.lastboluslabel;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.lastboluscompared, MainApp.gs(comparator.getValue().getStringRes()), (int) getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_bolus);
}
@Override
public Trigger duplicate() {
return new TriggerBolusAgo(this);
}
TriggerBolusAgo setValue(int requestedValue) {
this.minutesAgo.setMinutes(requestedValue);
return this;
}
TriggerBolusAgo lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerBolusAgo comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.lastboluslabel))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.lastboluslabel) + ": ", "", minutesAgo))
.build(root);
}
}

View file

@ -0,0 +1,153 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.T;
public class TriggerCOB extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private final int minValue = 0;
private final int maxValue = (int) (SP.getInt(R.string.key_treatmentssafety_maxcarbs, 48));
private final double step = 1;
private DecimalFormat decimalFormat = new DecimalFormat("1");
private InputDouble value = new InputDouble(0, (double) minValue, (double) maxValue, step, decimalFormat);
private Comparator comparator = new Comparator();
public TriggerCOB() {
super();
}
private TriggerCOB(TriggerCOB triggerCOB) {
super();
value = new InputDouble(triggerCOB.value);
lastRun = triggerCOB.lastRun;
comparator = new Comparator(triggerCOB.comparator);
}
public double getValue() {
return value.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
CobInfo cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "AutomationTriggerCOB");
if (cobInfo == null)
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
return true;
else
return false;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
boolean doRun = comparator.getValue().check((cobInfo.displayCob), getValue());
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerCOB.class.getName());
JSONObject data = new JSONObject();
data.put("carbs", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
value.setValue(JsonHelper.safeGetDouble(d, "carbs"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.triggercoblabel;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.cobcompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_cp_bolus_carbs);
}
@Override
public Trigger duplicate() {
return new TriggerCOB(this);
}
TriggerCOB setValue(int requestedValue) {
this.value.setValue(requestedValue);
return this;
}
TriggerCOB lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerCOB comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.triggercoblabel))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.triggercoblabel) + ": ", "", value))
.build(root);
}
}

View file

@ -0,0 +1,277 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.StringRes;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.dialogs.TriggerListAdapter;
import info.nightscout.androidaps.utils.JsonHelper;
public class TriggerConnector extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
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());
}
if (result)
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription().replace("\n", " "));
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");
list.clear();
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("\n").append(MainApp.gs(friendlyName())).append("\n");
result.append(t.friendlyDescription());
}
return result.toString();
}
@Override
public Optional<Integer> icon() {
return Optional.absent();
}
@Override
public void executed(long time) {
for (int i = 0; i < list.size(); ++i) {
list.get(i).executed(time);
}
}
@Override
public Trigger duplicate() {
return null;
}
private TriggerListAdapter adapter;
public void rebuildView(FragmentManager fragmentManager) {
if (adapter != null)
adapter.rebuild(fragmentManager);
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
final int padding = MainApp.dpToPx(5);
root.setPadding(padding, padding, padding, padding);
root.setBackgroundResource(R.drawable.border_automation_unit);
LinearLayout triggerListLayout = new LinearLayout(root.getContext());
triggerListLayout.setOrientation(LinearLayout.VERTICAL);
triggerListLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
root.addView(triggerListLayout);
adapter = new TriggerListAdapter(fragmentManager, root.getContext(), triggerListLayout, this);
}
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;
}
}

View file

@ -0,0 +1,208 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDelta;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDelta.DeltaType;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerDelta extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private double minValue = 0d;
private double maxValue = 1d;
private double step = 1;
private DecimalFormat decimalFormat = new DecimalFormat("1");
private String units;
private DeltaType deltaType;
private InputDelta value = new InputDelta( (double) minValue,(double) minValue, (double) maxValue, step, decimalFormat, deltaType);
private Comparator comparator = new Comparator();
public TriggerDelta() {
super();
this.units = ProfileFunctions.getInstance().getProfileUnits();
initializer();
}
private TriggerDelta(TriggerDelta triggerDelta) {
super();
this.units = ProfileFunctions.getInstance().getProfileUnits();
initializer();
value = triggerDelta.value;
lastRun = triggerDelta.lastRun;
}
public double getValue() {
deltaType = value.getDeltaType();
return value.getValue();
}
public DeltaType getType() {
return deltaType;
}
public String getUnits() {
return this.units;
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
if (glucoseStatus == null)
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
return true;
else
return false;
// Setting type of delta
double delta;
if (deltaType == DeltaType.SHORT_AVERAGE)
delta = glucoseStatus.short_avgdelta;
else if (deltaType == DeltaType.LONG_AVERAGE)
delta = glucoseStatus.long_avgdelta;
else
delta = glucoseStatus.delta;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
boolean doRun = comparator.getValue().check(delta, Profile.toMgdl(value.getValue(), this.units));
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: delta is " + delta + " " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerDelta.class.getName());
JSONObject data = new JSONObject();
data.put("value", getValue());
data.put("units", units);
data.put("lastRun", lastRun);
data.put("deltaType", getType());
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
units = JsonHelper.safeGetString(d, "units");
deltaType = DeltaType.valueOf(JsonHelper.safeGetString(d, "deltaType", ""));
value.setValue(JsonHelper.safeGetDouble(d, "value"), deltaType);
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.deltalabel;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.deltacompared, MainApp.gs(comparator.getValue().getStringRes()), getValue(), MainApp.gs(deltaType.getStringRes()));
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_auto_delta);
}
@Override
public Trigger duplicate() {
return new TriggerDelta(this);
}
TriggerDelta setValue(double requestedValue, DeltaType requestedType) {
this.value.setValue(requestedValue, requestedType);
this.deltaType = requestedType;
return this;
}
TriggerDelta setUnits(String units) {
this.units = units;
return this;
}
void initializer(){
if (this.units.equals(Constants.MMOL)) {
this.maxValue = 4d;
this.minValue = -4d;
this.step = 0.1d;
this.decimalFormat = new DecimalFormat("0.1");
this.deltaType = DeltaType.DELTA;
} else {
this.maxValue = 72d;
this.minValue = -72d;
this.step = 1d;
this.deltaType = DeltaType.DELTA;
}
value = new InputDelta( (double) minValue,(double) minValue, (double) maxValue, step, decimalFormat, deltaType);
}
TriggerDelta lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerDelta comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.deltalabel))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.deltalabel) + ": ", "", value))
.build(root);
}
}

View file

@ -0,0 +1,146 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputInsulin;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerIob extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private InputInsulin insulin = new InputInsulin();
private Comparator comparator = new Comparator();
public TriggerIob() {
super();
}
private TriggerIob(TriggerIob triggerIob) {
super();
insulin = new InputInsulin(triggerIob.insulin);
comparator = new Comparator(triggerIob.comparator);
lastRun = triggerIob.lastRun;
}
public double getValue() {
return insulin.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null)
return false;
IobTotal iob = IobCobCalculatorPlugin.getPlugin().calculateFromTreatmentsAndTempsSynchronized(DateUtil.now(), profile);
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
boolean doRun = comparator.getValue().check(iob.iob, getValue());
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerIob.class.getName());
JSONObject data = new JSONObject();
data.put("insulin", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
insulin.setValue(JsonHelper.safeGetDouble(d, "insulin"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.iob;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.iobcompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_keyboard_capslock);
}
@Override
public Trigger duplicate() {
return new TriggerIob(this);
}
TriggerIob setValue(double threshold) {
insulin.setValue(threshold);
return this;
}
TriggerIob lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerIob comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.iob))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.iob_u), "", insulin))
.build(root);
}
}

View file

@ -0,0 +1,167 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.location.Location;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.InputButton;
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
import info.nightscout.androidaps.plugins.general.automation.elements.InputString;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.services.LocationService;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerLocation extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
InputDouble latitude = new InputDouble(0d, -90d, +90d, 0.000001d, new DecimalFormat("0.000000"));
InputDouble longitude = new InputDouble(0d, -180d, +180d, 0.000001d, new DecimalFormat("0.000000"));
InputDouble distance = new InputDouble(200d, 0, 100000, 10d, new DecimalFormat("0"));
InputString name = new InputString();
private Runnable buttonAction = () -> {
Location location = LocationService.getLastLocation();
if (location != null) {
latitude.setValue(location.getLatitude());
longitude.setValue(location.getLongitude());
log.debug(String.format("Grabbed location: %f %f", latitude.getValue(), longitude.getValue()));
}
};
public TriggerLocation() {
super();
}
private TriggerLocation(TriggerLocation triggerLocation) {
super();
latitude = new InputDouble(triggerLocation.latitude.getValue(), -90d, +90d, 0.00001d, new DecimalFormat("0.00000"));
longitude = new InputDouble(triggerLocation.longitude.getValue(), -180d, +180d, 0.00001d, new DecimalFormat("0.00000"));
distance = new InputDouble(200d, 0, 100000, 10d, new DecimalFormat("0"));
lastRun = triggerLocation.lastRun;
}
@Override
public synchronized boolean shouldRun() {
Location location = LocationService.getLastLocation();
if (location == null)
return false;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
Location a = new Location("Trigger");
a.setLatitude(latitude.getValue());
a.setLongitude(longitude.getValue());
double calculatedDistance = location.distanceTo(a);
if (calculatedDistance < distance.getValue()) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerLocation.class.getName());
JSONObject data = new JSONObject();
data.put("latitude", latitude.getValue());
data.put("longitude", longitude.getValue());
data.put("distance", distance.getValue());
data.put("name", name.getValue());
data.put("lastRun", lastRun);
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
latitude.setValue(JsonHelper.safeGetDouble(d, "latitude"));
longitude.setValue(JsonHelper.safeGetDouble(d, "longitude"));
distance.setValue(JsonHelper.safeGetDouble(d, "distance"));
name.setValue(JsonHelper.safeGetString(d, "name"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.location;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.locationis, name.getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_location_on);
}
@Override
public Trigger duplicate() {
return new TriggerLocation(this);
}
TriggerLocation setLatitude(double value) {
latitude.setValue(value);
return this;
}
TriggerLocation setLongitude(double value) {
longitude.setValue(value);
return this;
}
TriggerLocation setdistance(double value) {
distance.setValue(value);
return this;
}
TriggerLocation lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.location))
.add(new LabelWithElement(MainApp.gs(R.string.name_short), "", name))
.add(new LabelWithElement(MainApp.gs(R.string.latitude_short), "", latitude))
.add(new LabelWithElement(MainApp.gs(R.string.longitude_short), "", longitude))
.add(new LabelWithElement(MainApp.gs(R.string.distance_short), "", distance))
.add(new InputButton(MainApp.gs(R.string.currentlocation), buttonAction), LocationService.getLastLocation() != null)
.build(root);
}
}

View file

@ -0,0 +1,152 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputPercent;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerProfilePercent extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private InputPercent pct = new InputPercent();
private Comparator comparator = new Comparator();
public TriggerProfilePercent() {
super();
}
private TriggerProfilePercent(TriggerProfilePercent triggerProfilePercent) {
super();
pct = new InputPercent(triggerProfilePercent.pct);
comparator = new Comparator(triggerProfilePercent.comparator);
lastRun = triggerProfilePercent.lastRun;
}
public double getValue() {
return pct.getValue();
}
public Comparator getComparator() {
return comparator;
}
public long getLastRun() {
return lastRun;
}
@Override
public synchronized boolean shouldRun() {
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null && comparator.getValue().equals(Comparator.Compare.IS_NOT_AVAILABLE)) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
if (profile == null)
return false;
boolean doRun = comparator.getValue().check((double) profile.getPercentage(), pct.getValue());
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerProfilePercent.class.getName());
JSONObject data = new JSONObject();
data.put("percentage", pct.getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
pct.setValue(JsonHelper.safeGetDouble(d, "percentage"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.profilepercentage;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.percentagecompared, MainApp.gs(comparator.getValue().getStringRes()), (int) pct.getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.icon_actions_profileswitch);
}
@Override
public Trigger duplicate() {
return new TriggerProfilePercent(this);
}
public TriggerProfilePercent setValue(double value) {
pct.setValue(value);
return this;
}
TriggerProfilePercent lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
public TriggerProfilePercent comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.profilepercentage))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.percent_u), "", pct))
.build(root);
}
}

View file

@ -0,0 +1,317 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.app.Activity;
import android.graphics.Typeface;
import android.text.format.DateFormat;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.FragmentManager;
import com.dpro.widgets.WeekdaysPicker;
import com.google.common.base.Optional;
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Objects;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerRecurringTime extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
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[] 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()];
}
@Nullable
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 getShortName() {
return shortNames[ordinal()];
}
}
private final boolean[] weekdays = new boolean[DayOfWeek.values().length];
// Recurring
private int hour;
private int minute;
private long validTo;
public TriggerRecurringTime() {
super();
setAll(false);
}
private TriggerRecurringTime(TriggerRecurringTime triggerTime) {
super();
lastRun = triggerTime.lastRun;
hour = triggerTime.hour;
minute = triggerTime.minute;
validTo = triggerTime.validTo;
if (weekdays.length >= 0)
System.arraycopy(triggerTime.weekdays, 0, weekdays, 0, weekdays.length);
}
public void setAll(boolean value) {
for (DayOfWeek day : DayOfWeek.values()) {
set(day, value);
}
}
public TriggerRecurringTime set(DayOfWeek day, boolean value) {
weekdays[day.ordinal()] = value;
return this;
}
private boolean isSet(DayOfWeek day) {
return weekdays[day.ordinal()];
}
@Override
public boolean shouldRun() {
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(Objects.requireNonNull(DayOfWeek.fromCalendarInt(scheduledDayOfWeek)))) {
if (DateUtil.now() >= scheduled && DateUtil.now() - scheduled < T.mins(5).msecs()) {
if (lastRun < scheduled) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
}
}
return false;
}
@Override
public String toJSON() {
JSONObject object = new JSONObject();
JSONObject data = new JSONObject();
try {
data.put("lastRun", lastRun);
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", TriggerRecurringTime.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");
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.recurringTime;
}
@Override
public String friendlyDescription() {
int counter = 0;
StringBuilder sb = new StringBuilder();
sb.append(MainApp.gs(R.string.every));
sb.append(" ");
for (Integer i : getSelectedDays()) {
if (counter > 0)
sb.append(",");
sb.append(MainApp.gs(Objects.requireNonNull(DayOfWeek.fromCalendarInt(i)).getShortName()));
counter++;
}
sb.append(" ");
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();
sb.append(DateUtil.timeString(scheduled));
if (counter == 0)
return MainApp.gs(R.string.never);
return sb.toString();
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_access_alarm_24dp);
}
@Override
public Trigger duplicate() {
return new TriggerRecurringTime(this);
}
TriggerRecurringTime lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
@SuppressWarnings("SameParameterValue")
TriggerRecurringTime validTo(long validTo) {
this.validTo = validTo;
return this;
}
TriggerRecurringTime hour(int hour) {
this.hour = hour;
return this;
}
TriggerRecurringTime 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 void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
TextView label = new TextView(root.getContext());
// TODO: Replace external tool WeekdaysPicker with a self-made GUI element
WeekdaysPicker weekdaysPicker = new WeekdaysPicker(root.getContext());
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);
TextView timeButton = new TextView(root.getContext());
GregorianCalendar runAt = new GregorianCalendar();
//Date runAt = new Date();
runAt.set(Calendar.HOUR_OF_DAY, hour);
runAt.set(Calendar.MINUTE, minute);
timeButton.setText(DateUtil.timeString(runAt.getTimeInMillis()));
timeButton.setOnClickListener(view -> {
TimePickerDialog tpd = TimePickerDialog.newInstance(
(view12, hourOfDay, minute, second) -> {
hour(hourOfDay);
minute(minute);
runAt.set(Calendar.HOUR_OF_DAY, hour);
runAt.set(Calendar.MINUTE, minute);
timeButton.setText(DateUtil.timeString(runAt.getTimeInMillis()));
},
runAt.get(Calendar.HOUR_OF_DAY),
runAt.get(Calendar.MINUTE),
DateFormat.is24HourFormat(root.getContext())
);
tpd.setThemeDark(true);
tpd.dismissOnPause(true);
Activity a = scanForActivity(root.getContext());
if (a != null)
tpd.show(a.getFragmentManager(), "TimePickerDialog");
});
int px = MainApp.dpToPx(10);
label.setText(MainApp.gs(R.string.atspecifiedtime, ""));
label.setTypeface(label.getTypeface(), Typeface.BOLD);
label.setPadding(px, px, px, px);
timeButton.setPadding(px, px, px, px);
LinearLayout l = new LinearLayout(root.getContext());
l.setOrientation(LinearLayout.HORIZONTAL);
l.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
l.addView(label);
l.addView(timeButton);
root.addView(l);
}
}

View file

@ -0,0 +1,131 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.ComparatorExists;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerTempTarget extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private ComparatorExists comparator = new ComparatorExists();
public TriggerTempTarget() {
super();
}
private TriggerTempTarget(TriggerTempTarget triggerTempTarget) {
super();
comparator = new ComparatorExists(triggerTempTarget.comparator);
lastRun = triggerTempTarget.lastRun;
}
public ComparatorExists getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
TempTarget tt = TreatmentsPlugin.getPlugin().getTempTargetFromHistory();
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
if (tt == null && comparator.getValue() == ComparatorExists.Compare.NOT_EXISTS) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
if (tt != null && comparator.getValue() == ComparatorExists.Compare.EXISTS) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerTempTarget.class.getName());
JSONObject data = new JSONObject();
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(ComparatorExists.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.temptarget;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.temptargetcompared, MainApp.gs(comparator.getValue().getStringRes()));
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_keyboard_tab);
}
@Override
public Trigger duplicate() {
return new TriggerTempTarget(this);
}
TriggerTempTarget lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
public TriggerTempTarget comparator(ComparatorExists.Compare compare) {
this.comparator = new ComparatorExists().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.temptarget))
.add(comparator)
.build(root);
}
}

View file

@ -0,0 +1,186 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.app.Activity;
import android.graphics.Typeface;
import android.text.format.DateFormat;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.GregorianCalendar;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerTime extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private long runAt;
public TriggerTime() {
runAt = DateUtil.now();
}
private TriggerTime(TriggerTime triggerTime) {
super();
lastRun = triggerTime.lastRun;
runAt = triggerTime.runAt;
}
@Override
public boolean shouldRun() {
long now = DateUtil.now();
if (now >= runAt && now - runAt < T.mins(5).msecs())
if (lastRun < runAt) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public String toJSON() {
JSONObject object = new JSONObject();
JSONObject data = new JSONObject();
try {
data.put("runAt", runAt);
data.put("lastRun", lastRun);
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");
} catch (JSONException e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.time;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.atspecifiedtime, DateUtil.dateAndTimeString(runAt));
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_access_alarm_24dp);
}
TriggerTime runAt(long runAt) {
this.runAt = runAt;
return this;
}
TriggerTime lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
@Override
public Trigger duplicate() {
return new TriggerTime(this);
}
long getRunAt() {
return runAt;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
TextView label = new TextView(root.getContext());
TextView dateButton = new TextView(root.getContext());
TextView timeButton = new TextView(root.getContext());
dateButton.setText(DateUtil.dateString(runAt));
timeButton.setText(DateUtil.timeString(runAt));
dateButton.setOnClickListener(view -> {
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(runAt);
DatePickerDialog dpd = DatePickerDialog.newInstance(
(view1, year, monthOfYear, dayOfMonth) -> {
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, monthOfYear);
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
runAt = calendar.getTimeInMillis();
dateButton.setText(DateUtil.dateString(runAt));
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
);
dpd.setThemeDark(true);
dpd.dismissOnPause(true);
Activity a = scanForActivity(root.getContext());
if (a != null)
dpd.show(a.getFragmentManager(), "DatePickerDialog");
});
timeButton.setOnClickListener(view -> {
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(runAt);
TimePickerDialog tpd = TimePickerDialog.newInstance(
(view12, hourOfDay, minute, second) -> {
calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
calendar.set(Calendar.MINUTE, minute);
runAt = calendar.getTimeInMillis();
timeButton.setText(DateUtil.timeString(runAt));
},
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
DateFormat.is24HourFormat(root.getContext())
);
tpd.setThemeDark(true);
tpd.dismissOnPause(true);
Activity a = scanForActivity(root.getContext());
if (a != null)
tpd.show(a.getFragmentManager(), "TimePickerDialog");
});
int px = MainApp.dpToPx(10);
label.setText(MainApp.gs(R.string.atspecifiedtime, ""));
label.setTypeface(label.getTypeface(), Typeface.BOLD);
label.setPadding(px, px, px, px);
dateButton.setPadding(px, px, px, px);
timeButton.setPadding(px, px, px, px);
LinearLayout l = new LinearLayout(root.getContext());
l.setOrientation(LinearLayout.HORIZONTAL);
l.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
l.addView(label);
l.addView(dateButton);
l.addView(timeButton);
root.addView(l);
}
}

View file

@ -0,0 +1,149 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventNetworkChange;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
import info.nightscout.androidaps.plugins.general.automation.elements.InputString;
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
import info.nightscout.androidaps.receivers.NetworkChangeReceiver;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.T;
public class TriggerWifiSsid extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
private InputString ssid = new InputString();
private Comparator comparator = new Comparator();
public TriggerWifiSsid() {
super();
}
private TriggerWifiSsid(TriggerWifiSsid triggerWifiSsid) {
super();
ssid = new InputString(triggerWifiSsid.ssid);
comparator = new Comparator(triggerWifiSsid.comparator);
lastRun = triggerWifiSsid.lastRun;
}
public String getValue() {
return ssid.getValue();
}
public Comparator getComparator() {
return comparator;
}
@Override
public synchronized boolean shouldRun() {
EventNetworkChange eventNetworkChange = NetworkChangeReceiver.getLastEvent();
if (eventNetworkChange == null)
return false;
if (lastRun > DateUtil.now() - T.mins(5).msecs())
return false;
if (!eventNetworkChange.wifiConnected && comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
boolean doRun = eventNetworkChange.wifiConnected && comparator.getValue().check(eventNetworkChange.ssid, getValue());
if (doRun) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Ready for execution: " + friendlyDescription());
return true;
}
return false;
}
@Override
public synchronized String toJSON() {
JSONObject o = new JSONObject();
try {
o.put("type", TriggerWifiSsid.class.getName());
JSONObject data = new JSONObject();
data.put("ssid", getValue());
data.put("lastRun", lastRun);
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
}
return o.toString();
}
@Override
Trigger fromJSON(String data) {
try {
JSONObject d = new JSONObject(data);
ssid.setValue(JsonHelper.safeGetString(d, "ssid"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public int friendlyName() {
return R.string.ns_wifi_ssids;
}
@Override
public String friendlyDescription() {
return MainApp.gs(R.string.wifissidcompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
}
@Override
public Optional<Integer> icon() {
return Optional.of(R.drawable.ic_network_wifi);
}
@Override
public Trigger duplicate() {
return new TriggerWifiSsid(this);
}
TriggerWifiSsid setValue(String value) {
ssid.setValue(value);
return this;
}
TriggerWifiSsid lastRun(long lastRun) {
this.lastRun = lastRun;
return this;
}
TriggerWifiSsid comparator(Comparator.Compare compare) {
this.comparator = new Comparator().setValue(compare);
return this;
}
@Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder()
.add(new StaticLabel(R.string.ns_wifi_ssids))
.add(comparator)
.add(new LabelWithElement(MainApp.gs(R.string.ns_wifi_ssids) + ": ", "", ssid))
.build(root);
}
}

View file

@ -74,7 +74,8 @@ public class Notification {
public static final int DST_IN_24H = 50;
public static final int DISKFULL = 51;
public static final int OLDVERSION = 52;
public static final int OVER_24H_TIME_CHANGE_REQUESTED = 53;
public static final int USERMESSAGE = 53;
public static final int OVER_24H_TIME_CHANGE_REQUESTED = 54;
public int id;

View file

@ -341,6 +341,22 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
return last;
}
public long getLastBolusTime(boolean isSMB) {
long now = System.currentTimeMillis();
long last = 0;
synchronized (treatments) {
for (Treatment t : treatments) {
if (!t.isValid)
continue;
if (t.date > last && t.insulin > 0 && t.isValid && t.date <= now && isSMB == t.isSMB)
last = t.date;
}
}
if (L.isEnabled(L.DATATREATMENTS))
log.debug("Last manual bolus time: " + new Date(last).toLocaleString());
return last;
}
@Override
public boolean isInHistoryRealTempBasalInProgress() {
return getRealTempBasalFromHistory(System.currentTimeMillis()) != null;

View file

@ -38,4 +38,8 @@ public class ChargingStateReceiver extends BroadcastReceiver {
static public boolean isCharging() {
return lastEvent != null && lastEvent.isCharging;
}
static public EventChargingState getLastEvent() {
return lastEvent;
}
}

View file

@ -70,4 +70,8 @@ public class NetworkChangeReceiver extends BroadcastReceiver {
public static boolean isWifiConnected() {
return lastEvent != null && lastEvent.wifiConnected;
}
public static EventNetworkChange getLastEvent() {
return lastEvent;
}
}

View file

@ -0,0 +1,150 @@
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 androidx.core.app.ActivityCompat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventLocationChange;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.T;
public class LocationService extends Service {
private static Logger log = LoggerFactory.getLogger(L.LOCATION);
private LocationManager mLocationManager = null;
private static final float LOCATION_DISTANCE = 10f;
private static final long LOCATION_INTERVAL_ACTIVE = T.mins(5).msecs();
private static final long LOCATION_INTERVAL_PASSIVE = T.mins(1).msecs(); // this doesn't cost more power
private static Location mLastLocation;
private class LocationListener implements android.location.LocationListener {
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);
RxBus.INSTANCE.send(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;
@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_ACTIVE,
LOCATION_DISTANCE,
mLocationListener = new LocationListener(LocationManager.NETWORK_PROVIDER)
);
if (SP.getString(R.string.key_location, "NONE").equals("GPS"))
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
LOCATION_INTERVAL_ACTIVE,
LOCATION_DISTANCE,
mLocationListener = new LocationListener(LocationManager.GPS_PROVIDER)
);
if (SP.getString(R.string.key_location, "NONE").equals("PASSIVE"))
mLocationManager.requestLocationUpdates(
LocationManager.PASSIVE_PROVIDER,
LOCATION_INTERVAL_PASSIVE,
LOCATION_DISTANCE,
mLocationListener = new LocationListener(LocationManager.PASSIVE_PROVIDER)
);
} 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();
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 - Provider: " + SP.getString(R.string.key_location, "NONE"));
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
public static Location getLastLocation() {
return mLastLocation;
}
}

View file

@ -120,11 +120,19 @@ public class DateUtil {
}
public static String timeString(Date date) {
return new DateTime(date).toString(DateTimeFormat.shortTime());
String format = "hh:mma";
if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) {
format = "HH:mm";
}
return new DateTime(date).toString(DateTimeFormat.forPattern(format));
}
public static String timeString(long mills) {
return new DateTime(mills).toString(DateTimeFormat.shortTime());
String format = "hh:mma";
if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) {
format = "HH:mm";
}
return new DateTime(mills).toString(DateTimeFormat.forPattern(format));
}
public static String timeFullString(long mills) {
@ -202,6 +210,10 @@ public class DateUtil {
return diff < T.mins(2).msecs();
}
public static GregorianCalendar gregorianCalendar() {
return new GregorianCalendar();
}
public static long getTimeZoneOffsetMs() {
return new GregorianCalendar().getTimeZone().getRawOffset();
}

View file

@ -33,6 +33,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);
}
EditText editText;
Button minusButton;
Button plusButton;
@ -49,6 +53,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;
@ -133,17 +138,21 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
@Override
public void afterTextChanged(Editable s) {
value = SafeParse.stringToDouble(editText.getText().toString());
callValueChangedListener();
if (okButton != null) {
if (value > maxValue || value < minValue)
okButton.setVisibility(INVISIBLE);
else
okButton.setVisibility(VISIBLE);
}
}
});
}
public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) {
mOnValueChangedListener = onValueChangedListener;
}
public void setTextWatcher(TextWatcher textWatcher) {
this.textWatcher = textWatcher;
editText.addTextChangedListener(textWatcher);
@ -182,6 +191,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener,
this.step = step;
this.formater = formater;
this.allowZero = allowZero;
callValueChangedListener();
this.okButton = okButton;
editText.setKeyListener(DigitsKeyListener.getInstance(minValue < 0, step != Math.rint(step)));
@ -197,6 +207,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);
@ -218,6 +229,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();
}
@ -228,6 +240,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();
}
@ -241,6 +254,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");

View file

@ -6,12 +6,19 @@ import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
import androidx.annotation.IdRes;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
public class ToastUtils {
public static void showToastInUiThread(final Context ctx,
final int stringId) {
showToastInUiThread(ctx, MainApp.gs(stringId));
}
public static void showToastInUiThread(final Context ctx,
final String string) {

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

View 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="#FFFFFFFF"
android:pathData="M12,8.41L16.59,13 18,11.59l-6,-6 -6,6L7.41,13 12,8.41zM6,18h12v-2H6v2z"/>
</vector>

View 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="#FFFFFFFF"
android:pathData="M11.59,7.41L15.17,11H1v2h14.17l-3.59,3.59L13,18l6,-6 -6,-6 -1.41,1.41zM20,6v12h2V6h-2z"/>
</vector>

View 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="#FFFFFFFF"
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>

View file

@ -0,0 +1,13 @@
<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.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
android:fillAlpha=".3"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M3.53,10.95l8.46,10.54 0.01,0.01 0.01,-0.01 8.46,-10.54C20.04,10.62 16.81,8 12,8c-4.81,0 -8.04,2.62 -8.47,2.95z"/>
</vector>

View 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="#FFFFFFFF"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

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

View 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>

View 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>

View 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="#FFFFFFFF"
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
</vector>

View file

@ -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>

View 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="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>

View 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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

View file

@ -0,0 +1,44 @@
<?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:layout_height="wrap_content"
android:layout_width="match_parent"
android:background="@color/ribbonDefault"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:layout_marginTop="8dp">
<ImageView
android:id="@+id/automation_iconTrash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_trash_outline"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:contentDescription="@string/overview_quickwizard_item_remove_button" />
<LinearLayout
android:id="@+id/automation_layoutText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_toStartOf="@id/automation_iconTrash"
android:layout_alignParentStart="true">
<TextView
android:id="@+id/automation_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" />
</LinearLayout>
</RelativeLayout>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/automation_actionTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingBottom="10dp"
android:textAppearance="?android:attr/textAppearanceLarge" />
<LinearLayout
android:id="@+id/automation_editActionLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:orientation="vertical"
android:padding="10dp" />
<include layout="@layout/okcancel" />
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog">
<LinearLayout
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="@string/please_choose_an_action_type" />
<RadioGroup
android:id="@+id/automation_radioGroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginVertical="5dp" />
<include layout="@layout/okcancel" />
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog">
<LinearLayout
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="@string/please_choose_a_trigger_type" />
<RadioGroup
android:id="@+id/automation_chooseTriggerRadioGroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginVertical="5dp" />
<include layout="@layout/okcancel" />
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog">
<LinearLayout
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/automation_inputEventTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/eventname" />
<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="@string/if_label"/>
<TextView
android:id="@+id/automation_editTrigger"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:text="@string/edit_short" />
</RelativeLayout>
<TextView
android:id="@+id/automation_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="@string/then_label"/>
<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="@string/edit_short" />
</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>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog">
<LinearLayout
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="@string/triggers" />
<LinearLayout
android:id="@+id/automation_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/okcancel" />
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:orientation="vertical"
android:padding="10dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/automation_inputEventTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/taskname" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginTop="5dp"
android:layout_marginBottom="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="@string/condition"
android:textStyle="bold" />
<TextView
android:id="@+id/automation_editTrigger"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:background="@color/ribbonDefault"
android:padding="8dp"
android:text="@string/edit_short" />
</RelativeLayout>
<TextView
android:id="@+id/automation_triggerDescription"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:orientation="vertical" />
<TextView
android:id="@+id/automation_forcedTriggerDescriptionLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/preconditions"
android:textStyle="bold" />
<TextView
android:id="@+id/automation_forcedTriggerDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:orientation="vertical" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginTop="5dp"
android:layout_marginBottom="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="@string/action"
android:textStyle="bold" />
<TextView
android:id="@+id/automation_addAction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:background="@color/ribbonDefault"
android:padding="8dp"
android:text="@string/add_short" />
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/automation_actionListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp" />
<include layout="@layout/okcancel" />
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,46 @@
<?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_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@color/ribbonDefault"
android:padding="8dp">
<ImageView
android:id="@+id/iconTrash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:contentDescription="@string/remove_label"
android:orientation="horizontal"
android:src="@drawable/ic_trash_outline" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_toStartOf="@id/iconTrash"
android:orientation="vertical"
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>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/automation_eventListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/automation_logView"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="-100dp" />
<TextView
android:id="@+id/automation_logView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/automation_fabAddEvent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:clickable="true"
android:focusable="true"
android:src="@drawable/ic_add_black_24dp"
tools:ignore="RelativeOverlap" />
</RelativeLayout>

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/done_background"
android:layout_width="match_parent"
android:layout_height="56dp"
android:orientation="horizontal"
android:background="@android:color/transparent"
android:gravity="end|right"
android:layout_gravity="center_vertical"
android:paddingBottom="8dp">
<Button
android:id="@+id/cancel"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
style="@style/mdtp_ActionButton.Text"
android:text="@string/mdtp_cancel" />
<Button
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
style="@style/mdtp_ActionButton.Text"
android:text="@string/mdtp_ok" />
</LinearLayout>
<!-- From: file:/Users/wdullaer/Documents/Programming%20Projects/MaterialDateTimePicker/library/src/main/res/layout/mdtp_done_button.xml -->

View file

@ -120,6 +120,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>

View file

@ -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>
@ -587,10 +587,12 @@
<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>
<string name="suspendloopfor10h">Suspend loop for 10 h</string>
<string name="suspendloopforXmin">Suspend loop for %1$d min</string>
<string name="disconnectpumpfor15m">Disconnect pump for 15 min</string>
<string name="disconnectpumpfor30m">Disconnect pump for 30 min</string>
<string name="disconnectpumpfor1h">Disconnect pump for 1 h</string>
@ -1308,6 +1310,47 @@
<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="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="stoptemptarget">Stop temp target</string>
<string name="islesser">is lesser than</string>
<string name="isequalorlesser">is equal or lesser 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="glucosecomparedmgdl">Glucose %1$s %2$.0f %3$s</string>
<string name="glucosecomparedmmol">Glucose %1$s %2$.1f %3$s</string>
<string name="percentagecompared">Profile pct %1$s %2$d</string>
<string name="iobcompared">IOB %1$s %2$.1f</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>
@ -1368,6 +1411,18 @@
<string name="sms_wrongcode">Wrong code. Command cancelled.</string>
<string name="notconfigured">Not configured</string>
<string name="profileswitchcreated">Profile switch created</string>
<string name="recurringTime">Recurring time</string>
<string name="every">Every</string>
<string name="never">Never</string>
<string name="mins">%1$dmins</string>
<string name="condition">Condition:</string>
<string name="action">Action:</string>
<string name="iob_u">IOB [U]:</string>
<string name="glucose_u">Glucose [%1$s]:</string>
<string name="delete_short">DEL</string>
<string name="add_short">ADD</string>
<string name="copy_short">COPY</string>
<string name="addnew">Add new</string>
<string name="versionChecker">Version Checker</string>
<string name="key_last_time_this_version_detected" translatable="false">last_time_this_version_detected</string>
<string name="key_last_versionchecker_warning" translatable="false">last_versionchecker_waring</string>
@ -1385,6 +1440,43 @@
<string name="dexcom_short">DXCM</string>
<string name="description_source_dexcom">Receive BG values from the patched Dexcom app.</string>
<string name="notification">Notification</string>
<string name="notification_message">Notification: %1$s</string>
<string name="message_short">Msg:</string>
<string name="profilepercentage">Profile percentage</string>
<string name="percent_u">Percent [%]:</string>
<string name="startprofile">Start profile %1$d%% for %2$d min</string>
<string name="startprofileforever">Start profile %1$d%%</string>
<string name="exists">exists</string>
<string name="notexists">not exists</string>
<string name="temptargetcompared">Temp target %1$s</string>
<string name="wifissidcompared">WiFi SSID %1$s %2$s</string>
<string name="autosenscompared">Autosens %1$s %2$s %%</string>
<string name="autosenslabel">Autosens %</string>
<string name="deltacompared">%3$s %1$s %2$s</string>
<string name="deltalabel">BG difference</string>
<string name="currentlocation">Current Location</string>
<string name="location">Location</string>
<string name="latitude_short">Lat:</string>
<string name="longitude_short">Lon:</string>
<string name="distance_short">Dist [m]:</string>
<string name="name_short">Name:</string>
<string name="locationis">Location is %1$s</string>
<string name="lastboluslabel">Last bolus ago</string>
<string name="lastboluscompared">Last bolus time %1$s %2$s min ago</string>
<string name="triggercoblabel">COB</string>
<string name="cobcompared">COB %1$s %2$.0f</string>
<string name="taskname">Task name</string>
<string name="eventname">Event name</string>
<string name="edit_short">EDIT</string>
<string name="please_choose_an_action_type">Please choose an action type:</string>
<string name="please_choose_a_trigger_type">Please choose a trigger type:</string>
<string name="if_label">If:</string>
<string name="then_label">Then:</string>
<string name="triggers">Triggers:</string>
<string name="remove_label">REMOVE</string>
<string name="preconditions">Preconditions:</string>
<!-- Pump Abstract -->
<string name="pump_operation_not_supported_by_pump_driver">Operation not supported by pump and/or driver.</string>
<string name="pump_operation_not_yet_supported_by_pump">Operation not YET supported by pump.</string>
@ -1569,4 +1661,4 @@
<item quantity="one">%1$d minute</item>
<item quantity="other">%1$d minutes</item>
</plurals>
</resources>
</resources>

View 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>

View file

@ -55,6 +55,8 @@ public class AAPSMocker {
public static Intent intentSent = null;
public static CommandQueue queue;
public static ConfigBuilderPlugin configBuilderPlugin;
public static ProfileFunctions profileFunctions;
public static ConstraintChecker constraintChecker;
public static void mockStrings() {
@ -147,6 +149,12 @@ public class AAPSMocker {
when(MainApp.gs(R.string.pumpsuspended)).thenReturn("Pump suspended");
when(MainApp.gs(R.string.cob)).thenReturn("COB");
when(MainApp.gs(R.string.value_unavailable_short)).thenReturn("n/a");
when(MainApp.gs(R.string.starttemptarget)).thenReturn("Start temp target");
when(MainApp.gs(R.string.stoptemptarget)).thenReturn("Stop temp target");
when(MainApp.gs(R.string.disableloop)).thenReturn("Disable loop");
when(MainApp.gs(R.string.enableloop)).thenReturn("Enable loop");
when(MainApp.gs(R.string.resumeloop)).thenReturn("Resume loop");
when(MainApp.gs(R.string.suspendloop)).thenReturn("Suspend loop");
when(MainApp.gs(R.string.pumpNotInitialized)).thenReturn("Pump not initialized!");
}
@ -160,7 +168,7 @@ public class AAPSMocker {
public static void mockConfigBuilder() {
PowerMockito.mockStatic(ConfigBuilderPlugin.class);
ConfigBuilderPlugin configBuilderPlugin = mock(ConfigBuilderPlugin.class);
configBuilderPlugin = mock(ConfigBuilderPlugin.class);
when(ConfigBuilderPlugin.getPlugin()).thenReturn(configBuilderPlugin);
}
@ -272,7 +280,7 @@ public class AAPSMocker {
public static void mockProfileFunctions() {
PowerMockito.mockStatic(ProfileFunctions.class);
ProfileFunctions profileFunctions = PowerMockito.mock(ProfileFunctions.class);
profileFunctions = PowerMockito.mock(ProfileFunctions.class);
PowerMockito.when(ProfileFunctions.getInstance()).thenReturn(profileFunctions);
profile = getValidProfile();
PowerMockito.when(ProfileFunctions.getInstance().getProfile()).thenReturn(profile);

Some files were not shown because too many files have changed in this diff Show more