From 1c94a20a5c0f71daa410fe8ea9af478e7ea7499e Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 29 Sep 2022 13:49:19 +0200 Subject: [PATCH 1/3] Resolve too many Widget threads --- .../main/java/info/nightscout/androidaps/widget/Widget.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/widget/Widget.kt b/app/src/main/java/info/nightscout/androidaps/widget/Widget.kt index 3d8c1e62cd..4ef921680d 100644 --- a/app/src/main/java/info/nightscout/androidaps/widget/Widget.kt +++ b/app/src/main/java/info/nightscout/androidaps/widget/Widget.kt @@ -54,7 +54,11 @@ class Widget : AppWidgetProvider() { @Inject lateinit var sp: SP @Inject lateinit var constraintChecker: ConstraintChecker - private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + companion object { + // This object doesn't behave like singleton, + // many threads were created. Making handler static resolve this issue + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + } private val intentAction = "OpenApp" override fun onReceive(context: Context, intent: Intent?) { From 9d5afe30cb7f53ead1e289db5b13f5a093b55691 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 29 Sep 2022 14:05:51 +0200 Subject: [PATCH 2/3] Insight: simplify code --- .../plugins/pump/insight/LocalInsightPlugin.java | 10 +++++----- .../pump/omnipod/eros/OmnipodErosPumpPlugin.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/insight/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java b/insight/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java index 503b68e53f..67bc30357d 100644 --- a/insight/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java +++ b/insight/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java @@ -1173,7 +1173,7 @@ public class LocalInsightPlugin extends PumpPluginBase implements Pump, Constrai } catch (Exception e) { aapsLogger.error("Exception while reading history", e); } - new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventRefreshOverview("LocalInsightPlugin::readHistory", false))); + rxBus.send(new EventRefreshOverview("LocalInsightPlugin::readHistory", false)); } private void processHistoryEvents(String serial, List historyEvents) { @@ -1612,7 +1612,7 @@ public class LocalInsightPlugin extends PumpPluginBase implements Pump, Constrai public void onStateChanged(InsightState state) { if (state == InsightState.CONNECTED) { statusLoaded = false; - new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventDismissNotification(Notification.INSIGHT_TIMEOUT_DURING_HANDSHAKE))); + rxBus.send(new EventDismissNotification(Notification.INSIGHT_TIMEOUT_DURING_HANDSHAKE)); } else if (state == InsightState.NOT_PAIRED) { connectionService.withdrawConnectionRequest(this); statusLoaded = false; @@ -1625,9 +1625,9 @@ public class LocalInsightPlugin extends PumpPluginBase implements Pump, Constrai activeTBR = null; activeBoluses = null; tbrOverNotificationBlock = null; - new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventRefreshOverview("LocalInsightPlugin::onStateChanged", false))); + rxBus.send(new EventRefreshOverview("LocalInsightPlugin::onStateChanged", false)); } - new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventLocalInsightUpdateGUI())); + rxBus.send(new EventLocalInsightUpdateGUI()); } @Override @@ -1638,7 +1638,7 @@ public class LocalInsightPlugin extends PumpPluginBase implements Pump, Constrai @Override public void onTimeoutDuringHandshake() { Notification notification = new Notification(Notification.INSIGHT_TIMEOUT_DURING_HANDSHAKE, rh.gs(R.string.timeout_during_handshake), Notification.URGENT); - new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventNewNotification(notification))); + rxBus.send(new EventNewNotification(notification)); } @Override diff --git a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java index f1c8882213..eec5d61dd9 100644 --- a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java +++ b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java @@ -282,7 +282,7 @@ public class OmnipodErosPumpPlugin extends PumpPluginBase implements Pump, Riley super.onStart(); if (handlerThread == null) { - handlerThread = new HandlerThread(OmnipodErosPumpPlugin.class.getSimpleName()); + handlerThread = new HandlerThread(OmnipodErosPumpPlugin.class.getSimpleName() + "Handler"); handlerThread.start(); loopHandler = new Handler(handlerThread.getLooper()); } From d0ea721e4e7481882cb48dd0ea6b651e2ae07696 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 29 Sep 2022 16:33:14 +0200 Subject: [PATCH 3/3] replace Thread() --- .../activities/ProfileHelperActivity.kt | 26 +++++-- .../androidaps/activities/StatsActivity.kt | 69 +++++++++++-------- .../plugins/aps/loop/LoopFragment.kt | 16 ++++- .../androidaps/plugins/aps/loop/LoopPlugin.kt | 26 +++++-- .../aps/openAPSAMA/OpenAPSAMAFragment.kt | 15 +++- .../aps/openAPSSMB/OpenAPSSMBFragment.kt | 15 +++- .../objectives/ObjectivesFragment.kt | 8 +-- .../SignatureVerifierPlugin.kt | 48 +++++++++---- .../general/autotune/AutotuneFragment.kt | 18 +++-- .../maintenance/MaintenanceFragment.kt | 30 ++++---- .../general/overview/OverviewFragment.kt | 43 ++++++++++-- 11 files changed, 219 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt index e3c21e15ad..576336e981 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt @@ -21,12 +21,17 @@ import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.defaultProfile.DefaultProfile import info.nightscout.androidaps.utils.defaultProfile.DefaultProfileDPV +import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.stats.TddCalculator +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DecimalFormat import javax.inject.Inject @@ -40,6 +45,8 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { @Inject lateinit var dateUtil: DateUtil @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var repository: AppRepository + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var fabricPrivacy: FabricPrivacy enum class ProfileType { MOTOL_DEFAULT, @@ -64,6 +71,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { private val profileSwitchUsed = arrayOf(0, 0) private lateinit var binding: ActivityProfilehelperBinding + private val disposable = CompositeDisposable() @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { @@ -158,13 +166,13 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { binding.basalPctFromTdd.setParams(32.0, 32.0, 37.0, 1.0, DecimalFormat("0"), false, null) binding.tdds.addView(TextView(this).apply { text = rh.gs(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress) }) - Thread { - val tdds = tddCalculator.stats(this) - runOnUiThread { - binding.tdds.removeAllViews() - binding.tdds.addView(tdds) - } - }.start() + disposable += Single.fromCallable { tddCalculator.stats(this) } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribe({ + binding.tdds.removeAllViews() + binding.tdds.addView(it) + }, fabricPrivacy::logException) // Current profile binding.currentProfileText.text = profileFunction.getProfileName() @@ -303,4 +311,8 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } } + override fun onPause() { + super.onPause() + disposable.clear() + } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt index 307f27afd6..748acc6699 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt @@ -9,10 +9,15 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.ActivityStatsBinding import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.utils.ActivityMonitor +import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.stats.DexcomTirCalculator import info.nightscout.androidaps.utils.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TirCalculator +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject class StatsActivity : NoSplashAppCompatActivity() { @@ -22,8 +27,11 @@ class StatsActivity : NoSplashAppCompatActivity() { @Inject lateinit var dexcomTirCalculator: DexcomTirCalculator @Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var fabricPrivacy: FabricPrivacy private lateinit var binding: ActivityStatsBinding + private val disposable = CompositeDisposable() @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { @@ -35,34 +43,34 @@ class StatsActivity : NoSplashAppCompatActivity() { binding.tir.addView(TextView(this).apply { text = getString(R.string.tir) + ": " + rh.gs(R.string.calculation_in_progress) }) binding.activity.addView(TextView(this).apply { text = getString(R.string.activitymonitor) + ": " + rh.gs(R.string.calculation_in_progress) }) - Thread { - val tdds = tddCalculator.stats(this) - runOnUiThread { - binding.tdds.removeAllViews() - binding.tdds.addView(tdds) - } - }.start() - Thread { - val tir = tirCalculator.stats(this) - runOnUiThread { - binding.tir.removeAllViews() - binding.tir.addView(tir) - } - }.start() - Thread { - val dexcomTir = dexcomTirCalculator.stats(this) - runOnUiThread { - binding.dexcomTir.removeAllViews() - binding.dexcomTir.addView(dexcomTir) - } - }.start() - Thread { - val activity = activityMonitor.stats(this) - runOnUiThread { - binding.activity.removeAllViews() - binding.activity.addView(activity) - } - }.start() + disposable += Single.fromCallable { tddCalculator.stats(this) } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribe({ + binding.tdds.removeAllViews() + binding.tdds.addView(it) + }, fabricPrivacy::logException) + disposable += Single.fromCallable { tirCalculator.stats(this) } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribe({ + binding.tir.removeAllViews() + binding.tir.addView(it) + }, fabricPrivacy::logException) + disposable += Single.fromCallable { dexcomTirCalculator.stats(this) } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribe({ + binding.dexcomTir.removeAllViews() + binding.dexcomTir.addView(it) + }, fabricPrivacy::logException) + disposable += Single.fromCallable { activityMonitor.stats(this) } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribe({ + binding.activity.removeAllViews() + binding.activity.addView(it) + }, fabricPrivacy::logException) binding.ok.setOnClickListener { finish() } binding.reset.setOnClickListener { @@ -73,4 +81,9 @@ class StatsActivity : NoSplashAppCompatActivity() { } } } + + override fun onPause() { + super.onPause() + disposable.clear() + } } 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 400080344a..1d44fa75d0 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 @@ -1,7 +1,14 @@ package info.nightscout.androidaps.plugins.aps.loop import android.os.Bundle -import android.view.* +import android.os.Handler +import android.os.HandlerThread +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.core.view.MenuProvider import androidx.lifecycle.Lifecycle import dagger.android.support.DaggerFragment @@ -34,8 +41,10 @@ class LoopFragment : DaggerFragment(), MenuProvider { @Inject lateinit var loop: Loop @Inject lateinit var dateUtil: DateUtil + @Suppress("PrivatePropertyName") private val ID_MENU_RUN = 501 + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) private var disposable: CompositeDisposable = CompositeDisposable() private var _binding: LoopFragmentBinding? = null @@ -57,7 +66,7 @@ class LoopFragment : DaggerFragment(), MenuProvider { setColorSchemeColors(rh.gac(context, R.attr.colorPrimaryDark), rh.gac(context, R.attr.colorPrimary), rh.gac(context, R.attr.colorSecondary)) setOnRefreshListener { binding.lastrun.text = rh.gs(R.string.executing) - Thread { loop.invoke("Loop swiperefresh", true) }.start() + handler.post { loop.invoke("Loop swiperefresh", true) } } } } @@ -71,7 +80,7 @@ class LoopFragment : DaggerFragment(), MenuProvider { when (item.itemId) { ID_MENU_RUN -> { binding.lastrun.text = rh.gs(R.string.executing) - Thread { loop.invoke("Loop menu", true) }.start() + handler.post { loop.invoke("Loop menu", true) } true } @@ -104,6 +113,7 @@ class LoopFragment : DaggerFragment(), MenuProvider { override fun onPause() { super.onPause() disposable.clear() + handler.removeCallbacksAndMessages(null) } @Synchronized diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt index a39d00d0bd..0eb6130f3e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt @@ -7,6 +7,8 @@ import android.app.PendingIntent import android.app.TaskStackBuilder import android.content.Context import android.content.Intent +import android.os.Handler +import android.os.HandlerThread import android.os.SystemClock import androidx.core.app.NotificationCompat import dagger.android.HasAndroidInjector @@ -33,8 +35,21 @@ import info.nightscout.androidaps.extensions.buildDeviceStatus import info.nightscout.androidaps.extensions.convertedToAbsolute import info.nightscout.androidaps.extensions.convertedToPercent import info.nightscout.androidaps.extensions.plannedRemainingMinutes -import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.CommandQueue +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.Loop.LastRun +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.PumpDescription +import info.nightscout.androidaps.interfaces.PumpSync +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui @@ -52,7 +67,6 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.T -import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag @@ -107,6 +121,8 @@ class LoopPlugin @Inject constructor( override var lastRun: LastRun? = null override var closedLoopEnabled: Constraint? = null + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + override fun onStart() { createNotificationChannel() super.onStart() @@ -128,6 +144,7 @@ class LoopPlugin @Inject constructor( override fun onStop() { disposable.clear() + handler.removeCallbacksAndMessages(null) super.onStop() } @@ -390,10 +407,7 @@ class LoopPlugin @Inject constructor( lastRun.lastSMBRequest = lastRun.lastAPSRun lastRun.lastSMBEnact = dateUtil.now() } else { - Thread { - SystemClock.sleep(1000) - invoke("tempBasalFallback", allowNotification, true) - }.start() + handler.postDelayed({ invoke("tempBasalFallback", allowNotification, true) }, 1000) } rxBus.send(EventLoopUpdateGui()) } 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 d135ef0cd0..0e59912e35 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 @@ -1,8 +1,15 @@ package info.nightscout.androidaps.plugins.aps.openAPSAMA import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread import android.text.TextUtils -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.core.view.MenuProvider import androidx.lifecycle.Lifecycle import dagger.android.support.DaggerFragment @@ -40,6 +47,7 @@ class OpenAPSAMAFragment : DaggerFragment(), MenuProvider { @Suppress("PrivatePropertyName") private val ID_MENU_RUN = 502 + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) private var _binding: OpenapsamaFragmentBinding? = null // This property is only valid between onCreateView and @@ -59,7 +67,7 @@ class OpenAPSAMAFragment : DaggerFragment(), MenuProvider { setColorSchemeColors(rh.gac(context, R.attr.colorPrimaryDark), rh.gac(context, R.attr.colorPrimary), rh.gac(context, R.attr.colorSecondary)) setOnRefreshListener { binding.lastrun.text = rh.gs(R.string.executing) - Thread { openAPSAMAPlugin.invoke("OpenAPSAMA swipe refresh", false) }.start() + handler.post { openAPSAMAPlugin.invoke("OpenAPSAMA swipe refresh", false) } } } @@ -74,7 +82,7 @@ class OpenAPSAMAFragment : DaggerFragment(), MenuProvider { when (item.itemId) { ID_MENU_RUN -> { binding.lastrun.text = rh.gs(R.string.executing) - Thread { openAPSAMAPlugin.invoke("OpenAPSAMA menu", false) }.start() + handler.post { openAPSAMAPlugin.invoke("OpenAPSAMA menu", false) } true } @@ -105,6 +113,7 @@ class OpenAPSAMAFragment : DaggerFragment(), MenuProvider { override fun onPause() { super.onPause() disposable.clear() + handler.removeCallbacksAndMessages(null) } @Synchronized 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 cb863b0fc7..0a8cd5f923 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 @@ -2,8 +2,15 @@ package info.nightscout.androidaps.plugins.aps.openAPSSMB import android.annotation.SuppressLint import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread import android.text.TextUtils -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.core.view.MenuProvider import androidx.lifecycle.Lifecycle import dagger.android.support.DaggerFragment @@ -43,6 +50,7 @@ class OpenAPSSMBFragment : DaggerFragment(), MenuProvider { private val ID_MENU_RUN = 503 private var _binding: OpenapsamaFragmentBinding? = null + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) // This property is only valid between onCreateView and // onDestroyView. @@ -61,7 +69,7 @@ class OpenAPSSMBFragment : DaggerFragment(), MenuProvider { setColorSchemeColors(rh.gac(context, R.attr.colorPrimaryDark), rh.gac(context, R.attr.colorPrimary), rh.gac(context, R.attr.colorSecondary)) setOnRefreshListener { binding.lastrun.text = rh.gs(R.string.executing) - Thread { activePlugin.activeAPS.invoke("OpenAPSSMB swipe refresh", false) }.start() + handler.post { activePlugin.activeAPS.invoke("OpenAPSSMB swipe refresh", false) } } } } @@ -75,7 +83,7 @@ class OpenAPSSMBFragment : DaggerFragment(), MenuProvider { when (item.itemId) { ID_MENU_RUN -> { binding.lastrun.text = rh.gs(R.string.executing) - Thread { activePlugin.activeAPS.invoke("OpenAPSSMB menu", false) }.start() + handler.post { activePlugin.activeAPS.invoke("OpenAPSSMB menu", false) } true } @@ -105,6 +113,7 @@ class OpenAPSSMBFragment : DaggerFragment(), MenuProvider { override fun onPause() { super.onPause() disposable.clear() + handler.removeCallbacksAndMessages(null) } @Synchronized diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index 09c8b50dc6..4287fd2743 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -245,7 +245,7 @@ class ObjectivesFragment : DaggerFragment() { rxBus.send(EventSWUpdate(false)) } else { // move out of UI thread - Thread { + handler.post { NtpProgressDialog().show((context as AppCompatActivity).supportFragmentManager, "NtpCheck") rxBus.send(EventNtpStatus(rh.gs(R.string.timedetection), 0)) sntpClient.ntpTime(object : SntpClient.Callback() { @@ -271,7 +271,7 @@ class ObjectivesFragment : DaggerFragment() { } } }, receiverStatusStore.isConnected) - }.start() + } } } holder.binding.start.setOnClickListener { @@ -284,7 +284,7 @@ class ObjectivesFragment : DaggerFragment() { rxBus.send(EventSWUpdate(false)) } else // move out of UI thread - Thread { + handler.post { NtpProgressDialog().show((context as AppCompatActivity).supportFragmentManager, "NtpCheck") rxBus.send(EventNtpStatus(rh.gs(R.string.timedetection), 0)) sntpClient.ntpTime(object : SntpClient.Callback() { @@ -306,7 +306,7 @@ class ObjectivesFragment : DaggerFragment() { } } }, receiverStatusStore.isConnected) - }.start() + } } holder.binding.unstart.setOnClickListener { activity?.let { activity -> diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.kt index aba2b0247f..2d3cb364e3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.kt @@ -2,6 +2,8 @@ package info.nightscout.androidaps.plugins.constraints.signatureVerifier import android.content.Context import android.content.pm.PackageManager +import android.os.Handler +import android.os.HandlerThread import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.Constraint @@ -9,19 +11,25 @@ import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType -import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification -import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.sharedPreferences.SP import org.spongycastle.util.encoders.Hex -import java.io.* +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream import java.net.URL import java.nio.charset.StandardCharsets import java.security.MessageDigest import java.security.NoSuchAlgorithmException -import java.util.* +import java.util.Arrays import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton @@ -39,15 +47,18 @@ class SignatureVerifierPlugin @Inject constructor( private val sp: SP, private val rxBus: RxBus, private val context: Context -) : PluginBase(PluginDescription() - .mainType(PluginType.CONSTRAINTS) - .neverVisible(true) - .alwaysEnabled(true) - .showInList(false) - .pluginName(R.string.signature_verifier), +) : PluginBase( + PluginDescription() + .mainType(PluginType.CONSTRAINTS) + .neverVisible(true) + .alwaysEnabled(true) + .showInList(false) + .pluginName(R.string.signature_verifier), aapsLogger, rh, injector ), Constraints { + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + private val REVOKED_CERTS_URL = "https://raw.githubusercontent.com/nightscout/AndroidAPS/master/app/src/main/assets/revoked_certs.txt" private val UPDATE_INTERVAL = TimeUnit.DAYS.toMillis(1) @@ -57,7 +68,7 @@ class SignatureVerifierPlugin @Inject constructor( override fun onStart() { super.onStart() revokedCertsFile = File(context.filesDir, "revoked_certs.txt") - Thread(Runnable { + handler.post { loadLocalRevokedCerts() if (shouldDownloadCerts()) { try { @@ -67,7 +78,12 @@ class SignatureVerifierPlugin @Inject constructor( } } if (hasIllegalSignature()) showNotification() - }).start() + } + } + + override fun onStop() { + handler.removeCallbacksAndMessages(null) + super.onStop() } override fun isLoopInvocationAllowed(value: Constraint): Constraint { @@ -76,13 +92,13 @@ class SignatureVerifierPlugin @Inject constructor( value.set(aapsLogger, false) } if (shouldDownloadCerts()) { - Thread(Runnable { + handler.post { try { downloadAndSaveRevokedCerts() } catch (e: IOException) { aapsLogger.error("Could not download revoked certs", e) } - }).start() + } } return value } @@ -143,7 +159,9 @@ class SignatureVerifierPlugin @Inject constructor( return hashes } - var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"§$%&/()=?,.-;:_<>|°^`´\\@€*'#+~{}[]¿¡áéíóúàèìòùöäü`ÁÉÍÓÚÀÈÌÒÙÖÄÜßÆÇÊËÎÏԌ۟æçêëîïôœûÿĆČĐŠŽćđšžñΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ\u03A2ΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρςστυφχψωϨϩϪϫϬϭϮϯϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗ" + var map = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"§$%&/()=?,.-;:_<>|°^`´\\@€*'#+~{}[]¿¡áéíóúàèìòùöäü`ÁÉÍÓÚÀÈÌÒÙÖÄÜßÆÇÊËÎÏԌ۟æçêëîïôœûÿĆČĐŠŽćđšžñΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ\u03A2ΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρςστυφχψωϨϩϪϫϬϭϮϯϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗ" + private fun singleCharMap(array: ByteArray): String { val sb = StringBuilder() for (b in array) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneFragment.kt index 57388f49cf..223e64c2ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/AutotuneFragment.kt @@ -4,6 +4,8 @@ import android.content.Context import android.graphics.Paint import android.graphics.Typeface import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread import android.text.Editable import android.text.TextWatcher import android.view.Gravity @@ -27,7 +29,12 @@ import info.nightscout.androidaps.databinding.AutotuneFragmentBinding import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.extensions.runOnUiThread import info.nightscout.androidaps.extensions.toVisibility -import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.ProfileStore +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile @@ -65,8 +72,8 @@ class AutotuneFragment : DaggerFragment() { @Inject lateinit var aapsSchedulers: AapsSchedulers private var disposable: CompositeDisposable = CompositeDisposable() + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) - //private val log = LoggerFactory.getLogger(AutotunePlugin::class.java) private var _binding: AutotuneFragmentBinding? = null private lateinit var profileStore: ProfileStore private var profileName = "" @@ -103,9 +110,7 @@ class AutotuneFragment : DaggerFragment() { val daysBack = SafeParse.stringToInt(binding.tuneDays.text) autotunePlugin.lastNbDays = daysBack.toString() log("Run Autotune $profileName, $daysBack days") - Thread { - autotunePlugin.aapsAutotune(daysBack, false, profileName) - }.start() + handler.post { autotunePlugin.aapsAutotune(daysBack, false, profileName) } updateGui() } binding.profileList.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ -> @@ -286,6 +291,7 @@ class AutotuneFragment : DaggerFragment() { override fun onPause() { super.onPause() disposable.clear() + handler.removeCallbacksAndMessages(null) } @Synchronized @@ -460,7 +466,7 @@ class AutotuneFragment : DaggerFragment() { setTextAppearance(android.R.style.TextAppearance_Material_Medium) } ) - layout.addView(toTableRowHeader(context,true)) + layout.addView(toTableRowHeader(context, true)) var totalPump = 0.0 var totalTuned = 0.0 for (h in 0 until tuned.basal.size) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt index d2085c0194..0284a57efb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt @@ -21,20 +21,22 @@ import info.nightscout.androidaps.interfaces.DataSyncSelector import info.nightscout.androidaps.interfaces.ImportExportPrefs import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.PumpSync -import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity import info.nightscout.androidaps.plugins.general.overview.OverviewData import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.database.DashHistoryDatabase import info.nightscout.androidaps.plugins.pump.omnipod.eros.history.database.ErosHistoryDatabase +import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.PREFERENCES -import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.rxjava3.core.Completable.fromAction +import info.nightscout.shared.logging.AAPSLogger +import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.subscribeBy import javax.inject.Inject @@ -58,8 +60,9 @@ class MaintenanceFragment : DaggerFragment() { @Inject lateinit var pumpSync: PumpSync @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var overviewData: OverviewData + @Inject lateinit var fabricPrivacy: FabricPrivacy - private val compositeDisposable = CompositeDisposable() + private val disposable = CompositeDisposable() private var inMenu = false private var queryingProtection = false private var _binding: MaintenanceFragmentBinding? = null @@ -79,16 +82,16 @@ class MaintenanceFragment : DaggerFragment() { updateProtectedUi() binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() } binding.logDelete.setOnClickListener { - uel.log(Action.DELETE_LOGS, Sources.Maintenance) - Thread { - maintenancePlugin.deleteLogs(5) - }.start() + disposable += + Completable.fromAction { maintenancePlugin.deleteLogs(5) } + .subscribeOn(aapsSchedulers.io) + .subscribe({ uel.log(Action.DELETE_LOGS, Sources.Maintenance) }, fabricPrivacy::logException) } binding.navResetdb.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.maintenance), rh.gs(R.string.reset_db_confirm), Runnable { - compositeDisposable.add( - fromAction { + disposable += + Completable.fromAction { repository.clearDatabases() danaHistoryDatabase.clearAllTables() insightDatabase.clearAllTables() @@ -104,11 +107,8 @@ class MaintenanceFragment : DaggerFragment() { .subscribeOn(aapsSchedulers.io) .subscribeBy( onError = { aapsLogger.error("Error clearing databases", it) }, - onComplete = { - rxBus.send(EventPreferenceChange(rh, R.string.key_units)) - } + onComplete = { rxBus.send(EventPreferenceChange(rh, R.string.key_units)) } ) - ) uel.log(Action.RESET_DATABASES, Sources.Maintenance) }) } @@ -148,7 +148,7 @@ class MaintenanceFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() - compositeDisposable.clear() + disposable.clear() _binding = null } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index 139e51f67d..664a03ef92 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -35,13 +35,42 @@ import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.databinding.OverviewFragmentBinding -import info.nightscout.androidaps.dialogs.* -import info.nightscout.androidaps.events.* +import info.nightscout.androidaps.dialogs.CalibrationDialog +import info.nightscout.androidaps.dialogs.CarbsDialog +import info.nightscout.androidaps.dialogs.InsulinDialog +import info.nightscout.androidaps.dialogs.LoopDialog +import info.nightscout.androidaps.dialogs.ProfileSwitchDialog +import info.nightscout.androidaps.dialogs.ProfileViewerDialog +import info.nightscout.androidaps.dialogs.TempTargetDialog +import info.nightscout.androidaps.dialogs.TreatmentDialog +import info.nightscout.androidaps.dialogs.WizardDialog +import info.nightscout.androidaps.events.EventAcceptOpenLoopChange +import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged +import info.nightscout.androidaps.events.EventExtendedBolusChange +import info.nightscout.androidaps.events.EventMobileToWear +import info.nightscout.androidaps.events.EventNewBG +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.events.EventPumpStatusChanged +import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.events.EventScale +import info.nightscout.androidaps.events.EventTempBasalChange +import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.extensions.directionToIcon import info.nightscout.androidaps.extensions.runOnUiThread import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.valueToUnitsString -import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.BuildHelper +import info.nightscout.androidaps.interfaces.CommandQueue +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.Loop +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB @@ -81,7 +110,7 @@ import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.weardata.EventData import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign -import java.util.* +import java.util.Locale import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.math.abs @@ -346,19 +375,23 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) TreatmentDialog().show(childFragmentManager, "Overview") }) + R.id.wizard_button -> protectionCheck.queryProtection( activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) WizardDialog().show(childFragmentManager, "Overview") }) + R.id.insulin_button -> protectionCheck.queryProtection( activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) InsulinDialog().show(childFragmentManager, "Overview") }) + R.id.quick_wizard_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) onClickQuickWizard() }) R.id.carbs_button -> protectionCheck.queryProtection( activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) CarbsDialog().show(childFragmentManager, "Overview") }) + R.id.temp_target -> protectionCheck.queryProtection( activity, ProtectionCheck.Protection.BOLUS, @@ -417,7 +450,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview) (context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?)?.cancel(Constants.notificationID) rxBus.send(EventMobileToWear(EventData.CancelNotification(dateUtil.now()))) - Thread { loop.acceptChangeRequest() }.start() + handler.post { loop.acceptChangeRequest() } binding.buttonsLayout.acceptTempButton.visibility = View.GONE }) })