adding, removing triggers properly

This commit is contained in:
Milos Kozak 2020-01-06 00:38:37 +01:00
parent e3766a229b
commit 1eb375af09
25 changed files with 244 additions and 280 deletions

View file

@ -30,7 +30,7 @@ class AutomationEvent(private val mainApp: MainApp) {
fun getPreconditions(): TriggerConnector { fun getPreconditions(): TriggerConnector {
val trigger = TriggerConnector(mainApp, TriggerConnector.Type.AND) val trigger = TriggerConnector(mainApp, TriggerConnector.Type.AND)
for (action in actions) { for (action in actions) {
action.precondition?.let { trigger.add(it) } action.precondition?.let { trigger.list.add(it) }
} }
return trigger return trigger
} }

View file

@ -213,6 +213,7 @@ class AutomationPlugin @Inject constructor(
fun getTriggerDummyObjects(): List<Trigger> { fun getTriggerDummyObjects(): List<Trigger> {
return listOf( return listOf(
TriggerConnector(mainApp),
TriggerTime(mainApp), TriggerTime(mainApp),
TriggerRecurringTime(mainApp), TriggerRecurringTime(mainApp),
TriggerTimeRange(mainApp), TriggerTimeRange(mainApp),

View file

@ -9,8 +9,16 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.dialogs.DialogFragmentWithDate import info.nightscout.androidaps.dialogs.DialogFragmentWithDate
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateTrigger import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateTrigger
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerChanged
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerClone
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerRemove
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.automation_dialog_edit_trigger.* import kotlinx.android.synthetic.main.automation_dialog_edit_trigger.*
import org.json.JSONObject import org.json.JSONObject
import javax.inject.Inject import javax.inject.Inject
@ -18,14 +26,17 @@ import javax.inject.Inject
class EditTriggerDialog : DialogFragmentWithDate() { class EditTriggerDialog : DialogFragmentWithDate() {
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var mainApp: MainApp @Inject lateinit var mainApp: MainApp
@Inject lateinit var fabricPrivacy: FabricPrivacy
private var trigger: Trigger? = null private var disposable: CompositeDisposable = CompositeDisposable()
private var triggers: Trigger? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View? {
// load data from bundle // load data from bundle
(savedInstanceState ?: arguments)?.let { bundle -> (savedInstanceState ?: arguments)?.let { bundle ->
bundle.getString("trigger")?.let { trigger = TriggerDummy(mainApp).instantiate(JSONObject(it)) } bundle.getString("trigger")?.let { triggers = TriggerDummy(mainApp).instantiate(JSONObject(it)) }
} }
onCreateViewGeneral() onCreateViewGeneral()
@ -35,17 +46,58 @@ class EditTriggerDialog : DialogFragmentWithDate() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
disposable += rxBus
.toObservable(EventTriggerChanged::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
automation_layoutTrigger.removeAllViews()
triggers?.generateDialog(automation_layoutTrigger)
}, { fabricPrivacy.logException(it) })
disposable += rxBus
.toObservable(EventTriggerRemove::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
findParent(triggers, it.trigger)?.list?.remove(it.trigger)
automation_layoutTrigger.removeAllViews()
triggers?.generateDialog(automation_layoutTrigger)
}, { fabricPrivacy.logException(it) })
disposable += rxBus
.toObservable(EventTriggerClone::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
findParent(triggers, it.trigger)?.list?.add(it.trigger.duplicate())
automation_layoutTrigger.removeAllViews()
triggers?.generateDialog(automation_layoutTrigger)
}, { fabricPrivacy.logException(it) })
// display root trigger // display root trigger
trigger?.generateDialog(automation_layoutTrigger) triggers?.generateDialog(automation_layoutTrigger)
}
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
} }
override fun submit(): Boolean { override fun submit(): Boolean {
trigger?.let { trigger -> rxBus.send(EventAutomationUpdateTrigger(trigger)) } triggers?.let { trigger -> rxBus.send(EventAutomationUpdateTrigger(trigger)) }
return true return true
} }
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
trigger?.let { savedInstanceState.putString("trigger", it.toJSON()) } triggers?.let { savedInstanceState.putString("trigger", it.toJSON()) }
}
private fun findParent(where: Trigger?, what: Trigger): TriggerConnector? {
if (where == null) return null
if (where is TriggerConnector) {
for (i in where.list) {
if (i == what) return where
if (i is TriggerConnector) return findParent(i, what)
}
}
return null
} }
} }

View file

@ -1,185 +0,0 @@
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;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
public class TriggerListAdapter {
private final LinearLayout mRootLayout;
private final Context mContext;
private final TriggerConnector rootTrigger;
private final MainApp mainApp;
private final ResourceHelper resourceHelper;
public TriggerListAdapter(MainApp mainApp, ResourceHelper resourceHelper, LinearLayout rootLayout, TriggerConnector rootTrigger) {
mRootLayout = rootLayout;
this.mainApp = mainApp;
this.resourceHelper = resourceHelper;
mContext = rootLayout.getContext();
this.rootTrigger = rootTrigger;
build(rootLayout.getContext());
}
private void destroy() {
mRootLayout.removeAllViews();
}
private void rebuild(Context context) {
destroy();
build(context);
}
private void build(Context context) {
FragmentManager fragmentManager = Trigger.scanForActivity(context).getSupportFragmentManager();
for (int i = 0; i < rootTrigger.size(); ++i) {
final Trigger trigger = rootTrigger.get(i);
// spinner
if (i > 0) {
createSpinner(trigger, context);
}
// trigger layout
trigger.generateDialog(mRootLayout);
// buttons
createButtons(mRootLayout, trigger, context);
}
if (rootTrigger.size() == 0) {
Button buttonAdd = new Button(mContext);
buttonAdd.setText(resourceHelper.gs(R.string.addnew));
buttonAdd.setOnClickListener(v -> {
ChooseTriggerDialog dialog = new ChooseTriggerDialog();
dialog.setOnClickListener(newTriggerObject -> {
rootTrigger.add(newTriggerObject);
rebuild(context);
});
dialog.show(fragmentManager, "ChooseTriggerDialog");
});
mRootLayout.addView(buttonAdd);
}
}
private Spinner createSpinner() {
Spinner spinner = new Spinner(mContext);
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(mContext, R.layout.spinner_centered, TriggerConnector.Type.AND.labels(resourceHelper));
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerArrayAdapter);
return spinner;
}
private void createSpinner(Trigger trigger, Context context) {
final TriggerConnector connector = trigger.getConnector();
final int initialPosition = connector.getConnectorType().ordinal();
Spinner spinner = createSpinner();
spinner.setSelection(initialPosition);
spinner.setBackgroundColor(resourceHelper.gc(R.color.black_overlay));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(0, resourceHelper.dpToPx(8), 0, resourceHelper.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(context, trigger, connector, TriggerConnector.Type.values()[position]);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
mRootLayout.addView(spinner);
}
private void createButtons(LinearLayout rootLayout, Trigger trigger, Context context) {
FragmentManager fragmentManager = Trigger.scanForActivity(context).getSupportFragmentManager();
// 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));
// Button [-]
Button buttonRemove = new Button(mContext);
buttonRemove.setText(resourceHelper.gs(R.string.delete_short));
buttonRemove.setOnClickListener(v -> {
final TriggerConnector connector = trigger.getConnector();
connector.remove(trigger);
connector.simplify();
rebuild(context);
});
buttonLayout.addView(buttonRemove);
// Button [+]
Button buttonAdd = new Button(mContext);
buttonAdd.setText(resourceHelper.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();
rebuild(context);
});
});
buttonLayout.addView(buttonAdd);
// Button [*]
Button buttonCopy = new Button(mContext);
buttonCopy.setText(resourceHelper.gs(R.string.copy_short));
buttonCopy.setOnClickListener(v -> {
TriggerConnector connector = trigger.getConnector();
connector.add(connector.pos(trigger) + 1, trigger.duplicate());
connector.simplify();
rebuild(context);
});
buttonLayout.addView(buttonCopy);
rootLayout.addView(buttonLayout);
}
public void changeConnector(final Context context, 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(mainApp, 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.setType(newConnectorType);
}
connector.simplify();
rebuild(context);
}
}

View file

@ -1,30 +1,45 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.graphics.Typeface import android.graphics.Typeface
import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
class StaticLabel(mainApp: MainApp) : Element(mainApp) { class StaticLabel(mainApp: MainApp) : Element(mainApp) {
var label = "" var label = ""
var trigger: Trigger? = null
constructor(mainApp: MainApp, label: String) : this(mainApp) { constructor(mainApp: MainApp, label: String, trigger: Trigger) : this(mainApp) {
this.label = label this.label = label
this.trigger = trigger
} }
constructor(mainApp: MainApp, resourceId: Int) : this(mainApp) { constructor(mainApp: MainApp, resourceId: Int, trigger: Trigger) : this(mainApp) {
label = resourceHelper.gs(resourceId) label = resourceHelper.gs(resourceId)
this.trigger = trigger
} }
override fun addToLayout(root: LinearLayout) { // text view pre element override fun addToLayout(root: LinearLayout) {
val headerLayout = LinearLayout(root.context)
headerLayout.orientation = LinearLayout.HORIZONTAL
headerLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
headerLayout.setBackgroundColor(resourceHelper.gc(android.R.color.black))
// text
val px = resourceHelper.dpToPx(10) val px = resourceHelper.dpToPx(10)
val textView = TextView(root.context) val textView = TextView(root.context)
textView.text = label textView.text = label
// textViewPre.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
params.weight = 1.0f
textView.layoutParams = params
textView.setPadding(px, px, px, px) textView.setPadding(px, px, px, px)
textView.setTypeface(textView.typeface, Typeface.BOLD) textView.setTypeface(textView.typeface, Typeface.BOLD)
textView.setBackgroundColor(resourceHelper.gc(R.color.mdtp_line_dark)) headerLayout.addView(textView)
root.addView(textView) trigger?.let {
headerLayout.addView(it.createDeleteButton(root.context, it))
headerLayout.addView(it.createCloneButton(root.context, it))
}
root.addView(headerLayout)
} }
} }

View file

@ -3,5 +3,4 @@ package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.general.automation.actions.Action import info.nightscout.androidaps.plugins.general.automation.actions.Action
class EventAutomationUpdateAction(val action: Action, val position : Int) : Event() { 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 EventTriggerChanged : 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 EventTriggerClone(val trigger: Trigger) : 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 EventTriggerRemove(val trigger: Trigger) : Event()

View file

@ -2,14 +2,22 @@ package info.nightscout.androidaps.plugins.general.automation.triggers
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.view.Gravity
import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.common.base.Optional import com.google.common.base.Optional
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction
import info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseTriggerDialog
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerChanged
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerClone
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerRemove
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.services.LocationService import info.nightscout.androidaps.services.LocationService
@ -22,6 +30,7 @@ import kotlin.reflect.full.primaryConstructor
abstract class Trigger(val mainApp: MainApp) { abstract class Trigger(val mainApp: MainApp) {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@ -30,8 +39,6 @@ abstract class Trigger(val mainApp: MainApp) {
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
var connector: TriggerConnector? = null
init { init {
mainApp.androidInjector().inject(this) mainApp.androidInjector().inject(this)
} }
@ -45,14 +52,15 @@ abstract class Trigger(val mainApp: MainApp) {
abstract fun icon(): Optional<Int?> abstract fun icon(): Optional<Int?>
abstract fun duplicate(): Trigger abstract fun duplicate(): Trigger
companion object { companion object {
@JvmStatic @JvmStatic
fun scanForActivity(cont: Context?): AppCompatActivity? { fun scanForActivity(cont: Context?): AppCompatActivity? {
if (cont == null) return null when (cont) {
else if (cont is AppCompatActivity) return cont null -> return null
else if (cont is ContextWrapper) return scanForActivity(cont.baseContext) is AppCompatActivity -> return cont
return null is ContextWrapper -> return scanForActivity(cont.baseContext)
else -> return null
}
} }
} }
@ -80,4 +88,64 @@ abstract class Trigger(val mainApp: MainApp) {
} }
return null return null
} }
fun createAddButton(context: Context, trigger: TriggerConnector): ImageButton {
// Button [+]
val buttonAdd = ImageButton(context)
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
params.gravity = Gravity.CENTER
buttonAdd.layoutParams = params
buttonAdd.setImageResource(R.drawable.add)
buttonAdd.contentDescription = resourceHelper.gs(R.string.add_short)
buttonAdd.setOnClickListener {
scanForActivity(context)?.supportFragmentManager?.let {
val dialog = ChooseTriggerDialog()
dialog.show(it, "ChooseTriggerDialog")
dialog.setOnClickListener(object : ChooseTriggerDialog.OnClickListener {
override fun onClick(newTriggerObject: Trigger) {
trigger.list.add(newTriggerObject)
rxBus.send(EventTriggerChanged())
}
})
}
}
return buttonAdd
}
fun createDeleteButton(context: Context, trigger: Trigger): ImageButton {
// Button [-]
val buttonRemove = ImageButton(context)
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
params.gravity = Gravity.CENTER
buttonRemove.layoutParams = params
buttonRemove.setImageResource(R.drawable.remove)
buttonRemove.contentDescription = resourceHelper.gs(R.string.delete_short)
buttonRemove.setOnClickListener {
rxBus.send(EventTriggerRemove(trigger))
}
return buttonRemove
}
fun createCloneButton(context: Context, trigger: Trigger): ImageButton {
// Button [*]
val buttonClone = ImageButton(context)
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
params.gravity = Gravity.CENTER
buttonClone.layoutParams = params
buttonClone.setImageResource(R.drawable.clone)
buttonClone.contentDescription = resourceHelper.gs(R.string.copy_short)
buttonClone.setOnClickListener {
rxBus.send(EventTriggerClone(trigger))
}
return buttonClone
}
} }

View file

@ -74,7 +74,7 @@ class TriggerAutosensValue(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.autosenslabel)) .add(StaticLabel(mainApp, R.string.autosenslabel, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.autosenslabel) + ": ", "", autosens)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.autosenslabel) + ": ", "", autosens))
.build(root) .build(root)

View file

@ -82,7 +82,7 @@ class TriggerBg(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.glucose)) .add(StaticLabel(mainApp, R.string.glucose, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.glucose_u, bg.units), "", bg)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.glucose_u, bg.units), "", bg))
.build(root) .build(root)

View file

@ -73,7 +73,7 @@ class TriggerBolusAgo(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.lastboluslabel)) .add(StaticLabel(mainApp, R.string.lastboluslabel, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.lastboluslabel) + ": ", "", minutesAgo)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.lastboluslabel) + ": ", "", minutesAgo))
.build(root) .build(root)

View file

@ -73,7 +73,7 @@ class TriggerCOB(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.triggercoblabel)) .add(StaticLabel(mainApp, R.string.triggercoblabel, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.triggercoblabel) + ": ", "", cob)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.triggercoblabel) + ": ", "", cob))
.build(root) .build(root)

View file

@ -1,13 +1,17 @@
package info.nightscout.androidaps.plugins.general.automation.triggers package info.nightscout.androidaps.plugins.general.automation.triggers
import android.content.Context
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Spinner
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.google.common.base.Optional import com.google.common.base.Optional
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.automation.dialogs.TriggerListAdapter
import info.nightscout.androidaps.utils.JsonHelper.safeGetString import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONArray import org.json.JSONArray
@ -16,7 +20,9 @@ import java.util.*
class TriggerConnector(mainApp: MainApp) : Trigger(mainApp) { class TriggerConnector(mainApp: MainApp) : Trigger(mainApp) {
var list: MutableList<Trigger> = ArrayList() var list: MutableList<Trigger> = ArrayList()
var connectorType: Type = Type.AND private var connectorType: Type = Type.AND
// TODO move to TriggerConnector
//var connector: TriggerConnector = TriggerConnector(mainApp, TriggerConnector.Type.AND)
enum class Type { enum class Type {
AND, OR, XOR; AND, OR, XOR;
@ -35,6 +41,7 @@ class TriggerConnector(mainApp: MainApp) : Trigger(mainApp) {
AND -> R.string.and AND -> R.string.and
} }
companion object {
fun labels(resourceHelper: ResourceHelper): List<String> { fun labels(resourceHelper: ResourceHelper): List<String> {
val list: MutableList<String> = ArrayList() val list: MutableList<String> = ArrayList()
for (t in values()) { for (t in values()) {
@ -43,6 +50,7 @@ class TriggerConnector(mainApp: MainApp) : Trigger(mainApp) {
return list return list
} }
} }
}
constructor(mainApp: MainApp, connectorType: Type) : this(mainApp) { constructor(mainApp: MainApp, connectorType: Type) : this(mainApp) {
this.connectorType = connectorType this.connectorType = connectorType
@ -52,25 +60,8 @@ class TriggerConnector(mainApp: MainApp) : Trigger(mainApp) {
connectorType = type connectorType = type
} }
@Synchronized
fun add(t: Trigger) {
list.add(t)
t.connector = this
}
@Synchronized
fun add(pos: Int, t: Trigger) {
list.add(pos, t)
t.connector = this
}
@Synchronized
fun remove(t: Trigger): Boolean = list.remove(t)
fun size(): Int = list.size fun size(): Int = list.size
operator fun get(i: Int): Trigger = list[i]
fun pos(trigger: Trigger): Int { fun pos(trigger: Trigger): Int {
for (i in list.indices) { for (i in list.indices) {
if (list[i] === trigger) return i if (list[i] === trigger) return i
@ -109,7 +100,7 @@ class TriggerConnector(mainApp: MainApp) : Trigger(mainApp) {
list.clear() list.clear()
for (i in 0 until array.length()) { for (i in 0 until array.length()) {
instantiate(JSONObject(array.getString(i)))?.let { instantiate(JSONObject(array.getString(i)))?.let {
add(it) list.add(it)
} }
} }
return this return this
@ -135,42 +126,48 @@ class TriggerConnector(mainApp: MainApp) : Trigger(mainApp) {
val padding = resourceHelper.dpToPx(5) val padding = resourceHelper.dpToPx(5)
root.setPadding(padding, padding, padding, padding) root.setPadding(padding, padding, padding, padding)
root.setBackgroundResource(R.drawable.border_automation_unit) root.setBackgroundResource(R.drawable.border_automation_unit)
// Header with spinner
val headerLayout = LinearLayout(root.context)
headerLayout.orientation = LinearLayout.HORIZONTAL
headerLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
headerLayout.addView(createSpinner(root.context))
headerLayout.addView(createAddButton(root.context, this))
headerLayout.addView(createDeleteButton(root.context, this))
root.addView(headerLayout)
// Child triggers
val listLayout = LinearLayout(root.context) val listLayout = LinearLayout(root.context)
listLayout.orientation = LinearLayout.VERTICAL listLayout.orientation = LinearLayout.VERTICAL
listLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) listLayout.setBackgroundColor(resourceHelper.gc(R.color.mdtp_line_dark))
//listLayout.setPadding(resourceHelper.dpToPx(5), resourceHelper.dpToPx(5), resourceHelper.dpToPx(5), 0)
val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
params.setMargins(resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(5), resourceHelper.dpToPx(4))
listLayout.layoutParams = params
for (t in list) t.generateDialog(listLayout)
root.addView(listLayout) root.addView(listLayout)
TriggerListAdapter(mainApp, resourceHelper, listLayout, this)
} }
fun simplify(): TriggerConnector { // simplify children private fun createSpinner(context: Context): Spinner {
for (i in 0 until size()) { val initialPosition = connectorType.ordinal
if (get(i) is TriggerConnector) { val spinner = Spinner(context)
val t = get(i) as TriggerConnector val spinnerArrayAdapter = ArrayAdapter(context, R.layout.spinner_centered, Type.labels(resourceHelper))
t.simplify() spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
} spinner.adapter = spinnerArrayAdapter
} spinner.setSelection(initialPosition)
// drop connector with only 1 element spinner.setBackgroundColor(resourceHelper.gc(R.color.black_overlay))
if (size() == 1 && get(0) is TriggerConnector) { val params = LinearLayout.LayoutParams(
val c = get(0) as TriggerConnector LinearLayout.LayoutParams.WRAP_CONTENT,
remove(c) LinearLayout.LayoutParams.WRAP_CONTENT
connectorType = c.connectorType )
for (t in c.list) add(t) params.setMargins(0, resourceHelper.dpToPx(8), 0, resourceHelper.dpToPx(8))
c.list.clear() params.weight = 1.0f
return simplify() spinner.layoutParams = params
} spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
// merge connectors override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
connector?.let { connector -> setType(Type.values()[position])
if (connector.connectorType == connectorType || size() == 1) { }
val pos = connector.pos(this)
connector.remove(this) override fun onNothingSelected(parent: AdapterView<*>?) {}
// move triggers of child connector into parent connector }
for (i in size() - 1 downTo 0) { return spinner
connector.add(pos, get(i))
}
list.clear()
return connector.simplify()
}
}
return this
} }
} }

View file

@ -98,7 +98,7 @@ class TriggerDelta(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.deltalabel)) .add(StaticLabel(mainApp, R.string.deltalabel, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.deltalabel_u, units) + ": ", "", delta)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.deltalabel_u, units) + ": ", "", delta))
.build(root) .build(root)

View file

@ -64,7 +64,7 @@ class TriggerIob(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.iob)) .add(StaticLabel(mainApp, R.string.iob, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.iob_u), "", insulin)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.iob_u), "", insulin))
.build(root) .build(root)

View file

@ -92,7 +92,7 @@ class TriggerLocation(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.location)) .add(StaticLabel(mainApp, R.string.location, this))
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.name_short), "", name)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.name_short), "", name))
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.latitude_short), "", latitude)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.latitude_short), "", latitude))
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.longitude_short), "", longitude)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.longitude_short), "", longitude))

View file

@ -73,7 +73,7 @@ class TriggerProfilePercent(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.profilepercentage)) .add(StaticLabel(mainApp, R.string.profilepercentage, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.percent_u), "", pct)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.percent_u), "", pct))
.build(root) .build(root)

View file

@ -73,7 +73,7 @@ class TriggerPumpLastConnection(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.automation_trigger_pump_last_connection_label)) .add(StaticLabel(mainApp, R.string.automation_trigger_pump_last_connection_label, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.automation_trigger_pump_last_connection_description) + ": ", "", minutesAgo)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.automation_trigger_pump_last_connection_description) + ": ", "", minutesAgo))
.build(root) .build(root)

View file

@ -94,7 +94,7 @@ class TriggerRecurringTime(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.recurringTime)) .add(StaticLabel(mainApp, R.string.recurringTime, this))
.add(days) .add(days)
.add(time) .add(time)
.build(root) .build(root)

View file

@ -62,7 +62,7 @@ class TriggerTempTarget(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.careportal_temporarytarget)) .add(StaticLabel(mainApp, R.string.careportal_temporarytarget, this))
.add(comparator) .add(comparator)
.build(root) .build(root)
} }

View file

@ -60,7 +60,7 @@ class TriggerTime(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.time)) .add(StaticLabel(mainApp, R.string.time, this))
.add(time) .add(time)
.build(root) .build(root)
} }

View file

@ -76,7 +76,7 @@ class TriggerTimeRange(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.time_range)) .add(StaticLabel(mainApp, R.string.time_range, this))
.add(range) .add(range)
.build(root) .build(root)
} }

View file

@ -44,7 +44,7 @@ class TriggerWifiSsid(mainApp: MainApp) : Trigger(mainApp) {
override fun toJSON(): String { override fun toJSON(): String {
val data = JSONObject() val data = JSONObject()
.put("ssid", ssid) .put("ssid", ssid.value)
.put("comparator", comparator.value.toString()) .put("comparator", comparator.value.toString())
return JSONObject() return JSONObject()
.put("type", this::class.java.name) .put("type", this::class.java.name)
@ -70,7 +70,7 @@ class TriggerWifiSsid(mainApp: MainApp) : Trigger(mainApp) {
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(mainApp, R.string.ns_wifi_ssids)) .add(StaticLabel(mainApp, R.string.ns_wifi_ssids, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(mainApp, resourceHelper.gs(R.string.ns_wifi_ssids) + ": ", "", ssid)) .add(LabelWithElement(mainApp, resourceHelper.gs(R.string.ns_wifi_ssids) + ": ", "", ssid))
.build(root) .build(root)