diff --git a/app/build.gradle b/app/build.gradle index 316c7cf77d..234d5a0c52 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -109,7 +109,7 @@ android { targetSdkVersion 28 multiDexEnabled true versionCode 1500 - version "2.4-dev-b" + version "2.4-dev-d" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b40188688c..aeeb53475d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -153,8 +153,7 @@ + android:exported="false" /> @@ -173,7 +172,7 @@ + android:exported="true" /> @@ -240,11 +239,11 @@ + android:exported="true" /> + android:exported="true" /> @@ -285,7 +284,7 @@ + android:exported="true" /> diff --git a/app/src/main/java/info/nightscout/androidaps/Constants.java b/app/src/main/java/info/nightscout/androidaps/Constants.java index f87e13d41f..adb3be4dc0 100644 --- a/app/src/main/java/info/nightscout/androidaps/Constants.java +++ b/app/src/main/java/info/nightscout/androidaps/Constants.java @@ -50,6 +50,11 @@ public class Constants { public static final double defaultHypoTTmgdl = 120d; public static final double defaultHypoTTmmol = 6.5d; + public static final double MIN_TT_MGDL = 72d; + public static final double MAX_TT_MGDL = 180d; + public static final double MIN_TT_MMOL = 4d; + public static final double MAX_TT_MMOL = 10d; + //NSClientInternal public static final int MAX_LOG_LINES = 100; diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index 2bcd3794be..ee3d50aa16 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -165,17 +165,18 @@ public class Profile { final JSONObject o = array.getJSONObject(index); long tas = 0; try { - tas = getShitfTimeSecs((int) o.getLong("timeAsSeconds")); - } catch (JSONException e) { String time = o.getString("time"); tas = getShitfTimeSecs(DateUtil.toSeconds(time)); + } catch (JSONException e) { //log.debug(">>>>>>>>>>>> Used recalculated timeAsSecons: " + time + " " + tas); + tas = getShitfTimeSecs((int) o.getLong("timeAsSeconds")); } double value = o.getDouble("value") * multiplier; sparse.put(tas, value); - } catch (JSONException e) { + } catch (Exception e) { log.error("Unhandled exception", e); log.error(json.toString()); + FabricPrivacy.logException(e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt index b4aa6b0991..2ab594c2a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt @@ -15,19 +15,15 @@ import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper +import info.nightscout.androidaps.utils.plusAssign import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.loop_fragment.* class LoopFragment : Fragment() { private var disposable: CompositeDisposable = CompositeDisposable() - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.loop_fragment, container, false) @@ -40,10 +36,9 @@ class LoopFragment : Fragment() { loop_lastrun.text = MainApp.gs(R.string.executing) Thread { LoopPlugin.getPlugin().invoke("Loop button", true) }.start() } - - updateGUI() } + @Synchronized override fun onResume() { super.onResume() disposable += RxBus @@ -64,14 +59,19 @@ class LoopFragment : Fragment() { }, { FabricPrivacy.logException(it) }) + + updateGUI() } + @Synchronized override fun onPause() { super.onPause() disposable.clear() } + @Synchronized fun updateGUI() { + if (loop_request == null) return LoopPlugin.lastRun?.let { loop_request.text = it.request?.toSpanned() ?: "" loop_constraintsprocessed.text = it.constraintsProcessed?.toSpanned() ?: "" @@ -96,7 +96,9 @@ class LoopFragment : Fragment() { } } + @Synchronized private fun clearGUI() { + if (loop_request == null) return loop_request.text = "" loop_constraints.text = "" loop_constraintsprocessed.text = "" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java index 47a477f382..0425190723 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java @@ -416,7 +416,7 @@ public class LoopPlugin extends PluginBase { .setAutoCancel(true) .setPriority(Notification.PRIORITY_HIGH) .setCategory(Notification.CATEGORY_ALARM) - .setVisibility(Notification.VISIBILITY_PUBLIC); + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); if (SP.getBoolean("wearcontrol", false)) { builder.setLocalOnly(true); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt index 763c93aa8c..94747a4597 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt @@ -15,9 +15,9 @@ import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.JSONFormatter +import info.nightscout.androidaps.utils.plusAssign import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.openapsama_fragment.* import org.json.JSONArray import org.json.JSONException @@ -27,10 +27,6 @@ class OpenAPSAMAFragment : Fragment() { private val log = LoggerFactory.getLogger(L.APS) private var disposable: CompositeDisposable = CompositeDisposable() - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.openapsama_fragment, container, false) @@ -42,10 +38,9 @@ class OpenAPSAMAFragment : Fragment() { openapsma_run.setOnClickListener { OpenAPSAMAPlugin.getPlugin().invoke("OpenAPSAMA button", false) } - - updateGUI() } + @Synchronized override fun onResume() { super.onResume() @@ -65,14 +60,19 @@ class OpenAPSAMAFragment : Fragment() { }, { FabricPrivacy.logException(it) }) + + updateGUI() } + @Synchronized override fun onPause() { super.onPause() disposable.clear() } + @Synchronized private fun updateGUI() { + if (openapsma_result == null) return OpenAPSAMAPlugin.getPlugin().lastAPSResult?.let { lastAPSResult -> openapsma_result.text = JSONFormatter.format(lastAPSResult.json) openapsma_request.text = lastAPSResult.toSpanned() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt index 6525641332..1b72f57365 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt @@ -13,9 +13,9 @@ import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.JSONFormatter +import info.nightscout.androidaps.utils.plusAssign import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.openapsama_fragment.* import org.slf4j.LoggerFactory @@ -23,10 +23,6 @@ class OpenAPSMAFragment : Fragment() { private val log = LoggerFactory.getLogger(L.APS) private var disposable: CompositeDisposable = CompositeDisposable() - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.openapsma_fragment, container, false) @@ -39,9 +35,9 @@ class OpenAPSMAFragment : Fragment() { OpenAPSMAPlugin.getPlugin().invoke("OpenAPSMA button", false) } - updateGUI() } + @Synchronized override fun onResume() { super.onResume() @@ -61,14 +57,18 @@ class OpenAPSMAFragment : Fragment() { }, { FabricPrivacy.logException(it) }) + updateGUI() } + @Synchronized override fun onPause() { super.onPause() disposable.clear() } - fun updateGUI() { + @Synchronized + private fun updateGUI() { + if (openapsma_result == null) return OpenAPSMAPlugin.getPlugin().lastAPSResult?.let { lastAPSResult -> openapsma_result.text = JSONFormatter.format(lastAPSResult.json) openapsma_request.text = lastAPSResult.toSpanned() @@ -85,7 +85,9 @@ class OpenAPSMAFragment : Fragment() { } } + @Synchronized private fun updateResultGUI(text: String) { + if (openapsma_result == null) return openapsma_result.text = text openapsma_glucosestatus.text = "" openapsma_currenttemp.text = "" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt index c102b65173..06ad0d01fd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.aps.openAPSSMB +import android.annotation.SuppressLint import android.os.Bundle import android.text.TextUtils import android.view.LayoutInflater @@ -15,9 +16,9 @@ import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.JSONFormatter +import info.nightscout.androidaps.utils.plusAssign import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.openapsama_fragment.* import org.json.JSONArray import org.json.JSONException @@ -27,10 +28,6 @@ class OpenAPSSMBFragment : Fragment() { private val log = LoggerFactory.getLogger(L.APS) private var disposable: CompositeDisposable = CompositeDisposable() - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.openapsama_fragment, container, false) @@ -42,10 +39,9 @@ class OpenAPSSMBFragment : Fragment() { openapsma_run.setOnClickListener { OpenAPSSMBPlugin.getPlugin().invoke("OpenAPSSMB button", false) } - - updateGUI() } + @Synchronized override fun onResume() { super.onResume() disposable += RxBus @@ -64,14 +60,19 @@ class OpenAPSSMBFragment : Fragment() { }, { FabricPrivacy.logException(it) }) + + updateGUI() } + @Synchronized override fun onPause() { super.onPause() disposable.clear() } + @Synchronized fun updateGUI() { + if (openapsma_result == null) return val plugin = OpenAPSSMBPlugin.getPlugin() plugin.lastAPSResult?.let { lastAPSResult -> openapsma_result.text = JSONFormatter.format(lastAPSResult.json) @@ -85,6 +86,7 @@ class OpenAPSSMBFragment : Fragment() { openapsma_iobdata.text = TextUtils.concat(String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) } catch (e: JSONException) { log.error("Unhandled exception", e) + @SuppressLint("SetTextl18n") openapsma_iobdata.text = "JSONException see log for details" } @@ -103,7 +105,9 @@ class OpenAPSSMBFragment : Fragment() { } } + @Synchronized private fun updateResultGUI(text: String) { + if (openapsma_result == null) return openapsma_result.text = text openapsma_glucosestatus.text = "" openapsma_currenttemp.text = "" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt index a8bcda3944..f011ac57cf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt @@ -44,10 +44,9 @@ class ConfigBuilderFragment : Fragment() { unlock.visibility = View.GONE }, null) } - - updateGUI() } + @Synchronized override fun onResume() { super.onResume() disposable.add(RxBus @@ -58,13 +57,16 @@ class ConfigBuilderFragment : Fragment() { }, { FabricPrivacy.logException(it) })) + updateGUI() } + @Synchronized override fun onPause() { super.onPause() disposable.clear() } + @Synchronized private fun updateGUI() { createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, MainApp.getSpecificPluginsVisibleInListByInterface(ProfileInterface::class.java, PluginType.PROFILE)) createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, MainApp.getSpecificPluginsVisibleInListByInterface(InsulinInterface::class.java, PluginType.INSULIN)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java index b92dadc89b..6eb3be382b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationEvent.java @@ -16,6 +16,7 @@ public class AutomationEvent { private Trigger trigger = new TriggerConnector(); private List actions = new ArrayList<>(); private String title; + private boolean enabled = true; public void setTitle(String title) { this.title = title; @@ -33,6 +34,14 @@ public class AutomationEvent { return actions; } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean newState) { + enabled = newState; + } + public TriggerConnector getPreconditions() { TriggerConnector trigger = new TriggerConnector(TriggerConnector.Type.AND); for (Action action : actions) { @@ -55,6 +64,7 @@ public class AutomationEvent { try { // title o.put("title", title); + o.put("enabled", enabled); // trigger o.put("trigger", trigger.toJSON()); // actions @@ -72,11 +82,9 @@ public class AutomationEvent { public AutomationEvent fromJSON(String data) { try { JSONObject d = new JSONObject(data); - // title title = d.optString("title", ""); - // trigger + enabled = d.optBoolean("enabled", true); trigger = Trigger.instantiate(d.getString("trigger")); - // actions JSONArray array = d.getJSONArray("actions"); actions.clear(); for (int i = 0; i < array.length(); i++) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt index 0bf996bd52..8a0789acea 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt @@ -12,9 +12,9 @@ import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDi import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.plusAssign 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() { @@ -22,10 +22,6 @@ 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) } @@ -48,19 +44,14 @@ class AutomationFragment : Fragment() { } + @Synchronized override fun onResume() { super.onResume() 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() + updateGui() }, { FabricPrivacy.logException(it) }) @@ -72,11 +63,25 @@ class AutomationFragment : Fragment() { }, { FabricPrivacy.logException(it) }) + updateGui() } + @Synchronized override fun onPause() { super.onPause() disposable.clear() } + @Synchronized + private fun updateGui() { + if (eventListAdapter == null) return + eventListAdapter?.notifyDataSetChanged() + val sb = StringBuilder() + for (l in AutomationPlugin.executionLog) { + sb.append(l) + sb.append("\n") + } + automation_logView.text = sb.toString() + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt index 2ab2c91411..00a76778d8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt @@ -21,12 +21,8 @@ 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.FabricPrivacy -import info.nightscout.androidaps.utils.SP -import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.* import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import org.json.JSONArray import org.json.JSONException @@ -60,10 +56,6 @@ object AutomationPlugin : PluginBase(PluginDescription() } } - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - override fun onStart() { val context = MainApp.instance().applicationContext if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) @@ -131,6 +123,7 @@ object AutomationPlugin : PluginBase(PluginDescription() loopHandler.removeCallbacks(refreshLoop) val context = MainApp.instance().applicationContext context.stopService(Intent(context, LocationService::class.java)) + super.onStop() } private fun storeToSP() { @@ -171,7 +164,7 @@ object AutomationPlugin : PluginBase(PluginDescription() if (L.isEnabled(L.AUTOMATION)) log.debug("processActions") for (event in automationEvents) { - if (event.trigger.shouldRun() && event.preconditions.shouldRun()) { + if (event.isEnabled() && event.trigger.shouldRun() && event.preconditions.shouldRun()) { val actions = event.actions for (action in actions) { action.doAction(object : Callback() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/EventListAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/EventListAdapter.java index 4bbd4a0959..b4c6fb1f11 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/EventListAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/EventListAdapter.java @@ -5,6 +5,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; @@ -20,8 +21,10 @@ import java.util.List; import info.nightscout.androidaps.MainApp; 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.dialogs.EditEventDialog; +import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged; import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector; class EventListAdapter extends RecyclerView.Adapter { @@ -51,6 +54,7 @@ class EventListAdapter extends RecyclerView.Adapter public void onBindViewHolder(@NonNull ViewHolder holder, int position) { final AutomationEvent event = mEventList.get(position); holder.eventTitle.setText(event.getTitle()); + holder.enabled.setChecked(event.isEnabled()); holder.iconLayout.removeAllViews(); // trigger icons @@ -77,6 +81,13 @@ class EventListAdapter extends RecyclerView.Adapter addImage(res, holder.context, holder.iconLayout); } + // enabled event + holder.enabled.setOnCheckedChangeListener((buttonView, isChecked) -> { + event.setEnabled(isChecked); + notifyDataSetChanged(); + RxBus.INSTANCE.send(new EventAutomationDataChanged()); + }); + // remove event holder.iconTrash.setOnClickListener(v -> { mEventList.remove(event); @@ -107,6 +118,7 @@ class EventListAdapter extends RecyclerView.Adapter final TextView eventTitle; final Context context; final ImageView iconTrash; + final CheckBox enabled; ViewHolder(View view, Context context) { super(view); @@ -115,6 +127,7 @@ class EventListAdapter extends RecyclerView.Adapter rootLayout = view.findViewById(R.id.rootLayout); iconLayout = view.findViewById(R.id.iconLayout); iconTrash = view.findViewById(R.id.iconTrash); + enabled = view.findViewById(R.id.automation_enabled); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java index 66f262485b..05d06f0b3a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.java @@ -15,8 +15,8 @@ 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.InputTempTarget; 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; @@ -27,7 +27,7 @@ import info.nightscout.androidaps.utils.JsonHelper; public class ActionStartTempTarget extends Action { String reason = ""; - InputBg value = new InputBg(); + InputTempTarget value = new InputTempTarget(); InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES); private TempTarget tempTarget; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputBg.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputBg.java index 7e1ff75962..0809ed96d8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputBg.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputBg.java @@ -11,27 +11,10 @@ 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) { - } - }; + static final int MMOL_MIN = 3; + static final int MMOL_MAX = 20; + static final int MGDL_MIN = 54; + static final int MGDL_MAX = 360; private String units = Constants.MGDL; private double value; @@ -43,6 +26,10 @@ public class InputBg extends Element { public InputBg() { super(); setUnits(ProfileFunctions.getInstance().getProfileUnits()); + if (getUnits().equals(Constants.MMOL)) + value = MMOL_MIN; + else + value = MGDL_MIN; } public InputBg(InputBg another) { @@ -55,7 +42,7 @@ public class InputBg extends Element { @Override public void addToLayout(LinearLayout root) { NumberPicker numberPicker = new NumberPicker(root.getContext(), null); - numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher); + numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, null); numberPicker.setOnValueChangedListener(value -> this.value = value); root.addView(numberPicker); } @@ -68,21 +55,18 @@ public class InputBg extends Element { // set default initial value if (units.equals(Constants.MMOL)) { // mmol - minValue = 2; - maxValue = 30; + minValue = MMOL_MIN; + maxValue = MMOL_MAX; step = 0.1; decimalFormat = new DecimalFormat("0.0"); } else { // mg/dL - minValue = 40; - maxValue = 540; + minValue = MGDL_MIN; + maxValue = MGDL_MAX; step = 1; decimalFormat = new DecimalFormat("0"); } - // make sure that value is in range - textWatcher.afterTextChanged(null); - this.units = units; return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDelta.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDelta.java index 10910220c3..ad0e84bec4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDelta.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputDelta.java @@ -21,19 +21,6 @@ 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, @@ -122,7 +109,7 @@ public class InputDelta extends Element { 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.setParams(value, minValue, maxValue, step, decimalFormat, true, null, null); numberPicker.setOnValueChangedListener(value -> this.value = value); LinearLayout l = new LinearLayout(root.getContext()); l.setOrientation(LinearLayout.VERTICAL); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTempTarget.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTempTarget.java new file mode 100644 index 0000000000..8175c496a5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputTempTarget.java @@ -0,0 +1,77 @@ +package info.nightscout.androidaps.plugins.general.automation.elements; + +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 InputTempTarget extends Element { + + private String units = Constants.MGDL; + private double value; + double minValue; + private double maxValue; + private double step; + private DecimalFormat decimalFormat; + + public InputTempTarget() { + super(); + setUnits(ProfileFunctions.getInstance().getProfileUnits()); + if (getUnits().equals(Constants.MMOL)) + value = Constants.MIN_TT_MMOL; + else + value = Constants.MIN_TT_MGDL; + } + + public InputTempTarget(InputTempTarget 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, null); + numberPicker.setOnValueChangedListener(value -> this.value = value); + root.addView(numberPicker); + } + + public String getUnits() { + return units; + } + + public InputTempTarget setUnits(String units) { + // set default initial value + if (units.equals(Constants.MMOL)) { + // mmol + minValue = Constants.MIN_TT_MMOL; + maxValue = Constants.MAX_TT_MMOL; + step = 0.1; + decimalFormat = new DecimalFormat("0.0"); + } else { + // mg/dL + minValue = Constants.MIN_TT_MGDL; + maxValue = Constants.MAX_TT_MGDL; + step = 1; + decimalFormat = new DecimalFormat("0"); + } + + this.units = units; + return this; + } + + public InputTempTarget setValue(double value) { + this.value = value; + return this; + } + + public double getValue() { + return value; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java index 00c3e21921..0b9edf148a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerDelta.java @@ -33,15 +33,15 @@ 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 final int MMOL_MAX = 4; + private final int MGDL_MAX = 72; + 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(); + private InputDelta value; + private Comparator comparator; public TriggerDelta() { super(); @@ -62,6 +62,16 @@ public class TriggerDelta extends Trigger { return value.getValue(); } + private void initializer() { + this.deltaType = DeltaType.DELTA; + comparator = new Comparator(); + if (units.equals(Constants.MMOL)) + value = new InputDelta(0, -MMOL_MAX, MMOL_MAX, 0.1d, new DecimalFormat("0.1"), DeltaType.DELTA); + else + value = new InputDelta(0, -MGDL_MAX, MGDL_MAX, 0.1d, new DecimalFormat("1"), DeltaType.DELTA); + } + + public DeltaType getType() { return deltaType; } @@ -150,7 +160,7 @@ public class TriggerDelta extends Trigger { @Override public Optional icon() { - return Optional.of(R.drawable.icon_auto_delta); + return Optional.of(R.drawable.icon_auto_delta); } @Override @@ -169,23 +179,6 @@ public class TriggerDelta extends Trigger { 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; @@ -201,7 +194,7 @@ public class TriggerDelta extends Trigger { new LayoutBuilder() .add(new StaticLabel(R.string.deltalabel)) .add(comparator) - .add(new LabelWithElement(MainApp.gs(R.string.deltalabel) + ": ", "", value)) + .add(new LabelWithElement(MainApp.gs(R.string.deltalabel_u, getUnits()) + ": ", "", value)) .build(root); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/Dialogs/NewNSTreatmentDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/Dialogs/NewNSTreatmentDialog.java index 91cabe35c1..3489180d9d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/Dialogs/NewNSTreatmentDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/Dialogs/NewNSTreatmentDialog.java @@ -263,17 +263,17 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick if (sensorRadioButton.isChecked()) meterRadioButton.setChecked(true); } }; - editBg = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_bginput); - editTemptarget = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_temptarget); + editBg = view.findViewById(R.id.careportal_newnstreatment_bginput); + editTemptarget = view.findViewById(R.id.careportal_newnstreatment_temptarget); if (profile == null) { - editBg.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok), bgTextWatcher); - editTemptarget.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok)); + editBg.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), bgTextWatcher); + editTemptarget.setParams(Constants.MIN_TT_MGDL, Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok)); } else if (units.equals(Constants.MMOL)) { editBg.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok), bgTextWatcher); - editTemptarget.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok)); + editTemptarget.setParams(Constants.MIN_TT_MMOL, Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok)); } else { editBg.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), bgTextWatcher); - editTemptarget.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok)); + editTemptarget.setParams(Constants.MIN_TT_MGDL, Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok)); } sensorRadioButton.setOnCheckedChangeListener((buttonView, isChecked) -> { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java index b5ff92ee0e..4b6072ee1e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java @@ -113,6 +113,7 @@ public class NSClientPlugin extends PluginBase { context.unbindService(mConnection); nsClientReceiverDelegate.unregisterReceivers(); + super.onStop(); } @Subscribe diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.java index 9633c8fbbe..4301ad6ec0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.java @@ -71,6 +71,7 @@ public class OverviewPlugin extends PluginBase { @Override protected void onStop() { MainApp.bus().unregister(this); + super.onStop(); } @Subscribe diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.java index 1400185afa..13310d70a8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.java @@ -31,7 +31,7 @@ public class DummyService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().updateNotification()); + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); return START_STICKY; } @@ -48,8 +48,7 @@ public class DummyService extends Service { super.onCreate(); // TODO: I guess this was moved here in order to adhere to the 5 seconds rule to call "startForeground" after a Service was called as Foreground service? // As onCreate() is not called every time a service is started, copied to onStartCommand(). - Notification notification = PersistentNotificationPlugin.getPlugin().updateNotification(); - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, notification); + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); MainApp.bus().register(this); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.java index e06dc741be..655435f400 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.java @@ -55,14 +55,13 @@ public class PersistentNotificationPlugin extends PluginBase { private Notification notification; public static PersistentNotificationPlugin getPlugin() { - if (plugin == null) plugin = new PersistentNotificationPlugin(MainApp.instance()); + if (plugin == null) plugin = new PersistentNotificationPlugin(); return plugin; } public static final String CHANNEL_ID = "AndroidAPS-Ongoing"; public static final int ONGOING_NOTIFICATION_ID = 4711; - private final Context ctx; /// For Android Auto /// Intents are not declared in manifest and not consumed, this is intentionally because actually we can't do anything with @@ -76,7 +75,7 @@ public class PersistentNotificationPlugin extends PluginBase { /// End Android Auto - private PersistentNotificationPlugin(Context ctx) { + private PersistentNotificationPlugin() { super(new PluginDescription() .mainType(PluginType.GENERAL) .neverVisible(true) @@ -86,22 +85,21 @@ public class PersistentNotificationPlugin extends PluginBase { .showInList(false) .description(R.string.description_persistent_notification) ); - this.ctx = ctx; } @Override protected void onStart() { + super.onStart(); createNotificationChannel(); // make sure channels exist before triggering updates through the bus MainApp.bus().register(this); - triggerNotificationUpdate(); - super.onStart(); + triggerNotificationUpdate(true); } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationManager mNotificationManager = - (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); + (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); @SuppressLint("WrongConstant") NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_HIGH); @@ -113,23 +111,26 @@ public class PersistentNotificationPlugin extends PluginBase { protected void onStop() { MainApp.bus().unregister(this); MainApp.instance().stopService(new Intent(MainApp.instance(), DummyService.class)); + super.onStop(); } - private void triggerNotificationUpdate() { + private void triggerNotificationUpdate(boolean boot) { + updateNotification(boot); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) MainApp.instance().startForegroundService(new Intent(MainApp.instance(), DummyService.class)); else MainApp.instance().startService(new Intent(MainApp.instance(), DummyService.class)); } - @Nonnull - public Notification updateNotification() { - String line1 = null; + private void updateNotification(boolean boot) { + String line1; String line2 = null; String line3 = null; NotificationCompat.CarExtender.UnreadConversation.Builder unreadConversationBuilder = null; - if (ConfigBuilderPlugin.getPlugin().getActiveProfileInterface() != null && ProfileFunctions.getInstance().isProfileValid("Notification")) { + if (boot) { + line1 = MainApp.gs(R.string.loading); + } else if (ConfigBuilderPlugin.getPlugin().getActiveProfileInterface() != null && ProfileFunctions.getInstance().isProfileValid("Notification")) { String line1_aa; String units = ProfileFunctions.getInstance().getProfileUnits(); @@ -185,7 +186,7 @@ public class PersistentNotificationPlugin extends PluginBase { .setPackage(PACKAGE); PendingIntent msgReadPendingIntent = - PendingIntent.getBroadcast(ctx, + PendingIntent.getBroadcast(MainApp.instance(), ONGOING_NOTIFICATION_ID, msgReadIntent, PendingIntent.FLAG_UPDATE_CURRENT); @@ -197,7 +198,7 @@ public class PersistentNotificationPlugin extends PluginBase { .setPackage(PACKAGE); PendingIntent msgReplyPendingIntent = PendingIntent.getBroadcast( - ctx, + MainApp.instance(), ONGOING_NOTIFICATION_ID, msgReplyIntent, PendingIntent.FLAG_UPDATE_CURRENT); @@ -215,18 +216,20 @@ public class PersistentNotificationPlugin extends PluginBase { /// Add dot to produce a "more natural sounding result" unreadConversationBuilder.addMessage(line3_aa); /// End Android Auto + } else { + line1 = MainApp.gs(R.string.noprofileset); } - NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx, CHANNEL_ID); + NotificationCompat.Builder builder = new NotificationCompat.Builder(MainApp.instance(), CHANNEL_ID); builder.setOngoing(true); builder.setOnlyAlertOnce(true); builder.setCategory(NotificationCompat.CATEGORY_STATUS); builder.setSmallIcon(MainApp.getNotificationIcon()); - Bitmap largeIcon = BitmapFactory.decodeResource(ctx.getResources(), MainApp.getIcon()); + Bitmap largeIcon = BitmapFactory.decodeResource(MainApp.instance().getResources(), MainApp.getIcon()); builder.setLargeIcon(largeIcon); - builder.setContentTitle(line1 != null ? line1 : MainApp.gs(R.string.noprofileset)); - builder.setContentText(line2 != null ? line2 : MainApp.gs(R.string.noprofileset)); - builder.setSubText(line3 != null ? line3 : MainApp.gs(R.string.noprofileset)); + if (line1 != null) builder.setContentTitle(line1); + if (line2 != null) builder.setContentText(line2); + if (line3 != null) builder.setSubText(line3); /// Android Auto if (unreadConversationBuilder != null) { builder.extend(new NotificationCompat.CarExtender() @@ -235,9 +238,9 @@ public class PersistentNotificationPlugin extends PluginBase { /// End Android Auto - Intent resultIntent = new Intent(ctx, MainActivity.class); + Intent resultIntent = new Intent(MainApp.instance(), MainActivity.class); - TaskStackBuilder stackBuilder = TaskStackBuilder.create(ctx); + TaskStackBuilder stackBuilder = TaskStackBuilder.create(MainApp.instance()); stackBuilder.addParentStack(MainActivity.class); stackBuilder.addNextIntent(resultIntent); PendingIntent resultPendingIntent = @@ -247,12 +250,11 @@ public class PersistentNotificationPlugin extends PluginBase { ); builder.setContentIntent(resultPendingIntent); NotificationManager mNotificationManager = - (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); + (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); android.app.Notification notification = builder.build(); mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification); this.notification = notification; - return notification; } private String deltastring(double deltaMGDL, double deltaMMOL, String units) { @@ -279,48 +281,50 @@ public class PersistentNotificationPlugin extends PluginBase { public Notification getLastNotification() { if (notification != null) return notification; - else return new Notification(); + else { + throw new IllegalStateException("Notification is null"); + } } @Subscribe public void onStatusEvent(final EventPreferenceChange ev) { - triggerNotificationUpdate(); + triggerNotificationUpdate(false); } @Subscribe public void onStatusEvent(final EventTreatmentChange ev) { - triggerNotificationUpdate(); + triggerNotificationUpdate(false); } @Subscribe public void onStatusEvent(final EventTempBasalChange ev) { - triggerNotificationUpdate(); + triggerNotificationUpdate(false); } @Subscribe public void onStatusEvent(final EventExtendedBolusChange ev) { - triggerNotificationUpdate(); + triggerNotificationUpdate(false); } @Subscribe public void onStatusEvent(final EventAutosensCalculationFinished ev) { - triggerNotificationUpdate(); + triggerNotificationUpdate(false); } @Subscribe public void onStatusEvent(final EventNewBasalProfile ev) { - triggerNotificationUpdate(); + triggerNotificationUpdate(false); } @Subscribe public void onStatusEvent(final EventInitializationChanged ev) { - triggerNotificationUpdate(); + triggerNotificationUpdate(false); } @Subscribe public void onStatusEvent(final EventRefreshOverview ev) { - triggerNotificationUpdate(); + triggerNotificationUpdate(false); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java index 14dcb169c4..095997daf9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.java @@ -98,6 +98,7 @@ public class SmsCommunicatorPlugin extends PluginBase { @Override protected void onStop() { MainApp.bus().unregister(this); + super.onStop(); } @Subscribe diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolFragment.kt index 86eb704bab..f83b9cc243 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolFragment.kt @@ -16,17 +16,12 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.SP import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.tidepool_fragment.* class TidepoolFragment : Fragment() { private var disposable: CompositeDisposable = CompositeDisposable() - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.tidepool_fragment, container, false) } @@ -39,6 +34,7 @@ class TidepoolFragment : Fragment() { tidepool_resertstart.setOnClickListener { SP.putLong(R.string.key_tidepool_last_end, 0) } } + @Synchronized override fun onResume() { super.onResume() disposable.add(RxBus @@ -46,16 +42,17 @@ class TidepoolFragment : Fragment() { .observeOn(AndroidSchedulers.mainThread()) .subscribe({ TidepoolPlugin.updateLog() - tidepool_log.text = TidepoolPlugin.textLog - tidepool_status.text = TidepoolUploader.connectionStatus.name - tidepool_log.text = TidepoolPlugin.textLog - tidepool_logscrollview.fullScroll(ScrollView.FOCUS_DOWN) + tidepool_log?.text = TidepoolPlugin.textLog + tidepool_status?.text = TidepoolUploader.connectionStatus.name + tidepool_log?.text = TidepoolPlugin.textLog + tidepool_logscrollview?.fullScroll(ScrollView.FOCUS_DOWN) }, { FabricPrivacy.logException(it) }) ) } + @Synchronized override fun onPause() { super.onPause() disposable.clear() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt index b7896d7e56..78ad0ab37d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.general.tidepool -import android.text.Html import android.text.Spanned import info.nightscout.androidaps.Constants import info.nightscout.androidaps.MainApp @@ -23,7 +22,6 @@ import info.nightscout.androidaps.receivers.ChargingStateReceiver import info.nightscout.androidaps.receivers.NetworkChangeReceiver import info.nightscout.androidaps.utils.* import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import org.slf4j.LoggerFactory import java.util.* @@ -43,10 +41,6 @@ object TidepoolPlugin : PluginBase(PluginDescription() private val listLog = ArrayList() var textLog: Spanned = HtmlHelper.fromHtml("") - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - override fun onStart() { super.onStart() disposable += RxBus diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.java index 69e327a21b..53b421ace3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.java @@ -91,6 +91,7 @@ public class WearPlugin extends PluginBase { protected void onStop() { MainApp.bus().unregister(this); disposable.clear(); + super.onStop(); } private void sendDataToWatch(boolean status, boolean basals, boolean bgValue) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt index 56abf0008c..7a76283074 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt @@ -90,9 +90,9 @@ class NSProfileFragment : Fragment() { } } } - updateGUI() } + @Synchronized override fun onResume() { super.onResume() disposable.add(RxBus @@ -104,14 +104,18 @@ class NSProfileFragment : Fragment() { FabricPrivacy.logException(it) }) ) + updateGUI() } + @Synchronized override fun onPause() { super.onPause() disposable.clear() } + @Synchronized fun updateGUI() { + if (profileview_noprofile == null) return profileview_noprofile.visibility = View.VISIBLE NSProfilePlugin.getPlugin().profile?.let { profileStore -> diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.java index 88faee377a..a6088f5515 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.java @@ -65,6 +65,7 @@ public class NSProfilePlugin extends PluginBase implements ProfileInterface { @Override protected void onStop() { MainApp.bus().unregister(this); + super.onStop(); } public void handleNewData(Intent intent) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java index 3deda538be..fa68e55616 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java @@ -100,6 +100,7 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter serviceRunning = false; MainApp.bus().unregister(this); + super.onStop(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt index 377e417274..11a8f13412 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt @@ -27,13 +27,9 @@ import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.fragments.ProfileViewerDialog import info.nightscout.androidaps.queue.events.EventQueueChanged -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.SetWarnColor -import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.* import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.danar_fragment.* import org.slf4j.LoggerFactory @@ -41,10 +37,6 @@ class DanaRFragment : Fragment() { private val log = LoggerFactory.getLogger(L.PUMP) private var disposable: CompositeDisposable = CompositeDisposable() - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - private val loopHandler = Handler() private lateinit var refreshLoop: Runnable @@ -84,9 +76,9 @@ class DanaRFragment : Fragment() { DanaRPump.getInstance().lastConnection = 0 ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("Clicked connect to pump", null) } - updateGUI() } + @Synchronized override fun onResume() { super.onResume() MainApp.bus().register(this) @@ -95,8 +87,10 @@ class DanaRFragment : Fragment() { .toObservable(EventDanaRNewStatus::class.java) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + updateGUI() } + @Synchronized override fun onPause() { super.onPause() disposable.clear() @@ -105,12 +99,12 @@ class DanaRFragment : Fragment() { } @Subscribe - public fun onStatusEvent(c: EventPumpStatusChanged) { + fun onStatusEvent(c: EventPumpStatusChanged) { activity?.runOnUiThread { when { - c.sStatus == EventPumpStatusChanged.CONNECTING -> danar_btconnection.text = "{fa-bluetooth-b spin} " + c.sSecondsElapsed + "s" - c.sStatus == EventPumpStatusChanged.CONNECTED -> danar_btconnection.text = "{fa-bluetooth}" - c.sStatus == EventPumpStatusChanged.DISCONNECTED -> danar_btconnection.text = "{fa-bluetooth-b}" + c.sStatus == EventPumpStatusChanged.CONNECTING -> danar_btconnection?.text = "{fa-bluetooth-b spin} " + c.sSecondsElapsed + "s" + c.sStatus == EventPumpStatusChanged.CONNECTED -> danar_btconnection?.text = "{fa-bluetooth}" + c.sStatus == EventPumpStatusChanged.DISCONNECTED -> danar_btconnection?.text = "{fa-bluetooth-b}" } if (c.textStatus() != "") { dana_pumpstatus.text = c.textStatus() @@ -123,20 +117,22 @@ class DanaRFragment : Fragment() { } @Subscribe - public fun onStatusEvent(s: EventTempBasalChange) = + fun onStatusEvent(s: EventTempBasalChange) = activity?.runOnUiThread { updateGUI() } @Subscribe - public fun onStatusEvent(s: EventExtendedBolusChange) = + fun onStatusEvent(s: EventExtendedBolusChange) = activity?.runOnUiThread { updateGUI() } @Subscribe - public fun onStatusEvent(s: EventQueueChanged) = + fun onStatusEvent(s: EventQueueChanged) = activity?.runOnUiThread { updateGUI() } // GUI functions + @Synchronized internal fun updateGUI() { + if (danar_dailyunits == null) return val pump = DanaRPump.getInstance() val plugin: PumpInterface = ConfigBuilderPlugin.getPlugin().activePump ?: return if (pump.lastConnection != 0L) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRPlugin.java index 80d0390390..6a01c5fed8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRPlugin.java @@ -65,6 +65,7 @@ public class DanaRPlugin extends AbstractDanaRPlugin { context.unbindService(mConnection); MainApp.bus().unregister(this); + super.onStop(); } private ServiceConnection mConnection = new ServiceConnection() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/DanaRKoreanPlugin.java index 07df7fb29f..64eccf05f1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/DanaRKoreanPlugin.java @@ -68,6 +68,7 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { context.unbindService(mConnection); MainApp.bus().unregister(this); + super.onStop(); } private ServiceConnection mConnection = new ServiceConnection() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java index 1e38ccc312..19005509cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java @@ -123,6 +123,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte context.unbindService(mConnection); MainApp.bus().unregister(this); + super.onStop(); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/DanaRv2Plugin.java index 65fb602ff4..607f7346b6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/DanaRv2Plugin.java @@ -71,6 +71,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { context.unbindService(mConnection); MainApp.bus().unregister(this); + super.onStop(); } private ServiceConnection mConnection = new ServiceConnection() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt index a77de2e4a4..d60a639106 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt @@ -35,13 +35,9 @@ import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.events.EventQueueChanged -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.SetWarnColor -import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.* import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import kotlinx.android.synthetic.main.medtronic_fragment.* import org.slf4j.LoggerFactory @@ -50,10 +46,6 @@ class MedtronicFragment : Fragment() { private val log = LoggerFactory.getLogger(L.PUMP) private var disposable: CompositeDisposable = CompositeDisposable() - operator fun CompositeDisposable.plusAssign(disposable: Disposable) { - add(disposable) - } - private val loopHandler = Handler() private lateinit var refreshLoop: Runnable @@ -94,7 +86,7 @@ class MedtronicFragment : Fragment() { MedtronicPumpPlugin.getPlugin().resetStatusState() ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("Clicked refresh", object : Callback() { override fun run() { - activity?.runOnUiThread { medtronic_refresh.isEnabled = true } + activity?.runOnUiThread { medtronic_refresh?.isEnabled = true } } }) } @@ -107,8 +99,6 @@ class MedtronicFragment : Fragment() { MedtronicUtil.displayNotConfiguredDialog(context) } } - - updateGUI() } @Synchronized @@ -141,6 +131,8 @@ class MedtronicFragment : Fragment() { MedtronicUtil.getPumpStatus().verifyConfiguration() updateGUI() }, { FabricPrivacy.logException(it) }) + + updateGUI() } @Synchronized diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java index a16178df18..f8568299d5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -1062,6 +1062,21 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter } + @Override + public PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes, Profile profile, + boolean enforceNew) { + if (percent==0) { + return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew); + } else { + double absoluteValue = profile.getBasal() * (percent /100.0d); + getMDTPumpStatus(); + absoluteValue = pumpStatusLocal.pumpType.determineCorrectBasalSize(absoluteValue); + LOG.warn("setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (%d). This will start setTempBasalAbsolute, with calculated value (%.3f). Result might not be 100% correct.", percent, absoluteValue); + return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew); + } + } + + private void finishAction(String overviewKey) { if (overviewKey != null) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.java index 1c2cf1f954..6b561bc41d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.java @@ -128,6 +128,7 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { @Override protected void onStop() { MainApp.bus().unregister(this); + super.onStop(); } @Subscribe diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java index a9284dd366..c1a26adcbd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java @@ -110,6 +110,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface @Override protected void onStop() { MainApp.bus().register(this); + super.onStop(); } public TreatmentService getService() { @@ -294,14 +295,19 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } + /** + * Returns all Treatments after specified timestamp. Also returns invalid entries (required to + * map "Fill Canulla" entries to history (and not to add double bolus for it) + * + * @param fromTimestamp + * @return + */ @Override public List getTreatmentsFromHistoryAfterTimestamp(long fromTimestamp) { List in5minback = new ArrayList<>(); long time = System.currentTimeMillis(); synchronized (treatments) { for (Treatment t : treatments) { - if (!t.isValid) - continue; if (t.date <= time && t.date >= fromTimestamp) in5minback.add(t); } diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java index 95fd6bfb09..6c2e7c6920 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java @@ -1,9 +1,8 @@ package info.nightscout.androidaps.receivers; -import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import androidx.legacy.content.WakefulBroadcastReceiver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,16 +10,15 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.services.DataService; -public class DataReceiver extends BroadcastReceiver { +public class DataReceiver extends WakefulBroadcastReceiver { private static Logger log = LoggerFactory.getLogger(L.DATASERVICE); @Override public void onReceive(Context context, Intent intent) { if (L.isEnabled(L.DATASERVICE)) log.debug("onReceive " + intent); - // Explicitly specify that GcmIntentService will handle the intent. - ComponentName comp = new ComponentName(context.getPackageName(), - DataService.class.getName()); - DataService.enqueueWork(context, intent.setComponent(comp)); + startWakefulService(context, new Intent(context, DataService.class) + .setAction(intent.getAction()) + .putExtras(intent)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/services/DataService.java b/app/src/main/java/info/nightscout/androidaps/services/DataService.java index 24a7698e9a..6b8ac46f11 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/services/DataService.java @@ -1,13 +1,10 @@ package info.nightscout.androidaps.services; -import android.content.Context; +import android.app.IntentService; import android.content.Intent; import android.os.Bundle; import android.provider.Telephony; -import androidx.annotation.NonNull; -import androidx.core.app.JobIntentService; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -19,16 +16,15 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.events.EventNsFood; import info.nightscout.androidaps.events.EventNsTreatment; -import info.nightscout.androidaps.logging.BundleLogger; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg; import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRNSHistorySync; +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin; import info.nightscout.androidaps.plugins.source.SourceEversensePlugin; import info.nightscout.androidaps.plugins.source.SourceGlimpPlugin; @@ -38,30 +34,21 @@ import info.nightscout.androidaps.plugins.source.SourcePoctechPlugin; import info.nightscout.androidaps.plugins.source.SourceTomatoPlugin; import info.nightscout.androidaps.plugins.source.SourceXdripPlugin; import info.nightscout.androidaps.receivers.DataReceiver; +import info.nightscout.androidaps.logging.BundleLogger; import info.nightscout.androidaps.utils.JsonHelper; import info.nightscout.androidaps.utils.SP; -public class DataService extends JobIntentService { +public class DataService extends IntentService { private Logger log = LoggerFactory.getLogger(L.DATASERVICE); - // Service unique ID - static final int SERVICE_JOB_ID = 4378; - - // Enqueuing work in to this service. - public static void enqueueWork(Context context, Intent work) { - enqueueWork(context, DataService.class, SERVICE_JOB_ID, work); + public DataService() { + super("DataService"); + registerBus(); } @Override - public void onDestroy() { - super.onDestroy(); - if (L.isEnabled(L.DATASERVICE)) - log.debug("All work complete"); - } - - @Override - protected void onHandleWork(@NonNull Intent intent) { + protected void onHandleIntent(final Intent intent) { if (L.isEnabled(L.DATASERVICE)) { log.debug("onHandleIntent " + intent); log.debug("onHandleIntent " + BundleLogger.log(intent.getExtras())); @@ -113,7 +100,7 @@ public class DataService extends JobIntentService { Intents.ACTION_REMOVED_TREATMENT.equals(action) || Intents.ACTION_NEW_CAL.equals(action) || Intents.ACTION_NEW_MBG.equals(action)) - ) { + ) { handleNewDataFromNSClient(intent); } else if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action)) { SmsCommunicatorPlugin.getPlugin().handleNewData(intent); @@ -121,6 +108,22 @@ public class DataService extends JobIntentService { if (L.isEnabled(L.DATASERVICE)) log.debug("onHandleIntent exit " + intent); + DataReceiver.completeWakefulIntent(intent); + } + + @Override + public void onDestroy() { + super.onDestroy(); + MainApp.bus().unregister(this); + } + + private void registerBus() { + try { + MainApp.bus().unregister(this); + } catch (RuntimeException x) { + // Ignore + } + MainApp.bus().register(this); } private void handleNewDataFromNSClient(Intent intent) { diff --git a/app/src/main/java/info/nightscout/androidaps/services/LocationService.java b/app/src/main/java/info/nightscout/androidaps/services/LocationService.java index feec4dcab8..33f92d770a 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/LocationService.java +++ b/app/src/main/java/info/nightscout/androidaps/services/LocationService.java @@ -11,10 +11,14 @@ import android.os.IBinder; import androidx.core.app.ActivityCompat; +import com.squareup.otto.Subscribe; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventLocationChange; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.bus.RxBus; @@ -80,12 +84,14 @@ public class LocationService extends Service { super.onStartCommand(intent, flags, startId); if (L.isEnabled(L.LOCATION)) log.debug("onStartCommand"); - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().updateNotification()); + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); return START_STICKY; } @Override public void onCreate() { + super.onCreate(); + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); if (L.isEnabled(L.LOCATION)) log.debug("onCreate"); @@ -119,6 +125,7 @@ public class LocationService extends Service { } catch (IllegalArgumentException ex) { log.error("network provider does not exist, " + ex.getMessage()); } + MainApp.bus().register(this); } @Override @@ -136,6 +143,15 @@ public class LocationService extends Service { log.error("fail to remove location listener, ignore", ex); } } + MainApp.bus().unregister(this); + } + + @Subscribe + public void onStatusEvent(EventAppExit event) { + if (L.isEnabled(L.CORE)) + log.debug("EventAppExit received"); + + stopSelf(); } private void initializeLocationManager() { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java index 7963ae0545..11d584bb87 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java @@ -3,6 +3,7 @@ package info.nightscout.androidaps.utils; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -29,6 +30,8 @@ public class AndroidPermission { public static final int CASE_BATTERY = 0x4; public static final int CASE_PHONE_STATE = 0x5; + private static boolean permission_battery_optimization_failed = false; + @SuppressLint("BatteryLife") private static void askForPermission(Activity activity, String[] permission, Integer requestCode) { boolean test = false; @@ -45,10 +48,15 @@ public class AndroidPermission { ActivityCompat.requestPermissions(activity, permission, requestCode); } if (testBattery) { - Intent i = new Intent(); - i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - i.setData(Uri.parse("package:" + activity.getPackageName())); - activity.startActivityForResult(i, CASE_BATTERY); + try { + Intent i = new Intent(); + i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + i.setData(Uri.parse("package:" + activity.getPackageName())); + activity.startActivityForResult(i, CASE_BATTERY); + } catch (ActivityNotFoundException e) { + permission_battery_optimization_failed = true; + OKDialog.show(activity, MainApp.gs(R.string.permission), MainApp.gs(R.string.alert_dialog_permission_battery_optimization_failed), activity::recreate); + } } } @@ -60,9 +68,11 @@ public class AndroidPermission { public static boolean permissionNotGranted(Context context, String permission) { boolean selfCheck = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; if (permission.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - String packageName = context.getPackageName(); - selfCheck = selfCheck && powerManager.isIgnoringBatteryOptimizations(packageName); + if (!permission_battery_optimization_failed) { + PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + String packageName = context.getPackageName(); + selfCheck = selfCheck && powerManager.isIgnoringBatteryOptimizations(packageName); + } } return !selfCheck; } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/CompositeDisposablePlusAssign.kt b/app/src/main/java/info/nightscout/androidaps/utils/CompositeDisposablePlusAssign.kt new file mode 100644 index 0000000000..1f2fb892e5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/CompositeDisposablePlusAssign.kt @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.utils + +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable + +operator fun CompositeDisposable.plusAssign(disposable: Disposable) { + add(disposable) +} + diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DigitsKeyListenerWithComma.java b/app/src/main/java/info/nightscout/androidaps/utils/DigitsKeyListenerWithComma.java new file mode 100644 index 0000000000..a84ccd61a2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/DigitsKeyListenerWithComma.java @@ -0,0 +1,199 @@ +package info.nightscout.androidaps.utils; + +import android.text.InputType; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.method.NumberKeyListener; +import android.view.KeyEvent; + +class DigitsKeyListenerWithComma extends NumberKeyListener { + + /** + * The characters that are used. + * + * @see KeyEvent#getMatch + * @see #getAcceptedChars + */ + private static final char[][] CHARACTERS = new char[][]{ + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}, + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'}, + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', ','}, + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', ','}, + }; + + private char[] mAccepted; + private boolean mSign; + private boolean mDecimal; + + private static final int SIGN = 1; + private static final int DECIMAL = 2; + + private static DigitsKeyListenerWithComma[] sInstance = new DigitsKeyListenerWithComma[4]; + + @Override + protected char[] getAcceptedChars() { + return mAccepted; + } + + /** + * Allocates a DigitsKeyListener that accepts the digits 0 through 9. + */ + public DigitsKeyListenerWithComma() { + this(false, false); + } + + /** + * Allocates a DigitsKeyListener that accepts the digits 0 through 9, + * plus the minus sign (only at the beginning) and/or decimal point + * (only one per field) if specified. + */ + public DigitsKeyListenerWithComma(boolean sign, boolean decimal) { + mSign = sign; + mDecimal = decimal; + + int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0); + mAccepted = CHARACTERS[kind]; + } + + /** + * Returns a DigitsKeyListener that accepts the digits 0 through 9. + */ + public static DigitsKeyListenerWithComma getInstance() { + return getInstance(false, false); + } + + /** + * Returns a DigitsKeyListener that accepts the digits 0 through 9, + * plus the minus sign (only at the beginning) and/or decimal point + * (only one per field) if specified. + */ + public static DigitsKeyListenerWithComma getInstance(boolean sign, boolean decimal) { + int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0); + + if (sInstance[kind] != null) + return sInstance[kind]; + + sInstance[kind] = new DigitsKeyListenerWithComma(sign, decimal); + return sInstance[kind]; + } + + /** + * Returns a DigitsKeyListener that accepts only the characters + * that appear in the specified String. Note that not all characters + * may be available on every keyboard. + */ + public static DigitsKeyListenerWithComma getInstance(String accepted) { + // TODO: do we need a cache of these to avoid allocating? + + DigitsKeyListenerWithComma dim = new DigitsKeyListenerWithComma(); + + dim.mAccepted = new char[accepted.length()]; + accepted.getChars(0, accepted.length(), dim.mAccepted, 0); + + return dim; + } + + public int getInputType() { + int contentType = InputType.TYPE_CLASS_NUMBER; + if (mSign) { + contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED; + } + if (mDecimal) { + contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + } + return contentType; + } + + @Override + public CharSequence filter(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + CharSequence out = super.filter(source, start, end, dest, dstart, dend); + + if (mSign == false && mDecimal == false) { + return out; + } + + if (out != null) { + source = out; + start = 0; + end = out.length(); + } + + int sign = -1; + int decimal = -1; + int dlen = dest.length(); + + /* + * Find out if the existing text has '-' or '.' characters. + */ + + for (int i = 0; i < dstart; i++) { + char c = dest.charAt(i); + + if (c == '-') { + sign = i; + } else if (c == '.' || c == ',') { + decimal = i; + } + } + for (int i = dend; i < dlen; i++) { + char c = dest.charAt(i); + + if (c == '-') { + return ""; // Nothing can be inserted in front of a '-'. + } else if (c == '.' || c == ',') { + decimal = i; + } + } + + /* + * If it does, we must strip them out from the source. + * In addition, '-' must be the very first character, + * and nothing can be inserted before an existing '-'. + * Go in reverse order so the offsets are stable. + */ + + SpannableStringBuilder stripped = null; + + for (int i = end - 1; i >= start; i--) { + char c = source.charAt(i); + boolean strip = false; + + if (c == '-') { + if (i != start || dstart != 0) { + strip = true; + } else if (sign >= 0) { + strip = true; + } else { + sign = i; + } + } else if (c == '.' || c == ',') { + if (decimal >= 0) { + strip = true; + } else { + decimal = i; + } + } + + if (strip) { + if (end == start + 1) { + return ""; // Only one character, and it was stripped. + } + + if (stripped == null) { + stripped = new SpannableStringBuilder(source, start, end); + } + + stripped.delete(i - start, i + 1 - start); + } + } + + if (stripped != null) { + return stripped; + } else if (out != null) { + return out; + } else { + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java b/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java index b125d0b2a3..e2a7489fc2 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java @@ -183,7 +183,8 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, } setParams(initValue, minValue, maxValue, step, formater, allowZero, okButton); this.textWatcher = textWatcher; - editText.addTextChangedListener(textWatcher); + if (textWatcher != null) + editText.addTextChangedListener(textWatcher); } public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formater, boolean allowZero, Button okButton) { @@ -196,7 +197,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, callValueChangedListener(); this.okButton = okButton; - editText.setKeyListener(DigitsKeyListener.getInstance(minValue < 0, step != Math.rint(step))); + editText.setKeyListener(DigitsKeyListenerWithComma.getInstance(minValue < 0, step != Math.rint(step))); if (textWatcher != null) editText.removeTextChangedListener(textWatcher); diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java index 436043fd36..be8b89dff4 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java @@ -80,6 +80,7 @@ public class TimeListEdit { private void buildView() { layout = view.findViewById(resLayoutId); + layout.removeAllViewsInLayout(); textlabel = new TextView(context); textlabel.setText(label); @@ -235,8 +236,8 @@ public class TimeListEdit { if (i == 0) next = ONEHOURINSECONDS; fillSpinner(timeSpinner, secondFromMidnight(i), previous, next); - editText1.setParams(value1(i), min, max, step, formatter, false, view.findViewById(R.id.localprofile_save)); - editText2.setParams(value2(i), min, max, step, formatter, false, view.findViewById(R.id.localprofile_save)); + editText1.setParams(value1(i), min, max, step, formatter, false,null); + editText2.setParams(value2(i), min, max, step, formatter, false, null); if (data2 == null) { editText2.setVisibility(View.GONE); @@ -350,7 +351,7 @@ public class TimeListEdit { data1.put(index, newObject1); if (data2 != null) { JSONObject newObject2 = new JSONObject(); - newObject1.put("time", time); + newObject2.put("time", time); newObject2.put("timeAsSeconds", timeAsSeconds); newObject2.put("value", value2); data2.put(index, newObject2); diff --git a/app/src/main/res/layout/automation_event_item.xml b/app/src/main/res/layout/automation_event_item.xml index ce377a6aa0..6cbc138278 100644 --- a/app/src/main/res/layout/automation_event_item.xml +++ b/app/src/main/res/layout/automation_event_item.xml @@ -11,6 +11,27 @@ android:background="@color/ribbonDefault" android:padding="8dp"> + + + + + android:layout_below="@id/automation_enabled" + android:orientation="horizontal" /> - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/localprofile_fragment.xml b/app/src/main/res/layout/localprofile_fragment.xml index be5f994302..1edeeb0b31 100644 --- a/app/src/main/res/layout/localprofile_fragment.xml +++ b/app/src/main/res/layout/localprofile_fragment.xml @@ -1,197 +1,194 @@ - - + android:layout_height="wrap_content" + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - +