diff --git a/app/build.gradle b/app/build.gradle index afd61512638..89496541039 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -109,7 +109,7 @@ android { defaultConfig { multiDexEnabled true versionCode 1500 - version "3.0.0.1-dev-a" + version "3.0.0.1-dev-c" 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 92f076e4347..a163b3729c1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -64,6 +64,7 @@ @@ -75,8 +76,7 @@ - + + android:label="@string/title_activity_setup_wizard" /> 10 && currenttemp.duration ) { diff --git a/app/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js b/app/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js index 13d66a859fe..061ef879d07 100644 --- a/app/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js @@ -1247,4 +1247,4 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ }; -module.exports = determine_basal; +module.exports = determine_basal; \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt index 48296330cf2..f88ac370e77 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt @@ -56,8 +56,8 @@ import info.nightscout.androidaps.utils.tabs.TabPageAdapter import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* import javax.inject.Inject import kotlin.system.exitProcess @@ -320,7 +320,7 @@ class MainActivity : NoSplashAppCompatActivity() { message += rh.gs(R.string.about_link_urls) val messageSpanned = SpannableString(message) Linkify.addLinks(messageSpanned, Linkify.WEB_URLS) - AlertDialog.Builder(this) + AlertDialog.Builder(this, R.style.DialogTheme) .setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION) .setIcon(iconsProvider.getIcon()) .setMessage(messageSpanned) diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.kt b/app/src/main/java/info/nightscout/androidaps/MainApp.kt index dbebf4e6357..781ab707c36 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.kt @@ -39,10 +39,10 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.locale.LocaleHelper import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.exceptions.UndeliverableException -import io.reactivex.plugins.RxJavaPlugins -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.exceptions.UndeliverableException +import io.reactivex.rxjava3.kotlin.plusAssign +import io.reactivex.rxjava3.plugins.RxJavaPlugins import net.danlew.android.joda.JodaTimeAndroid import java.io.IOException import java.net.SocketException diff --git a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt index 7883c4ecd23..3cf77eaf742 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt @@ -42,8 +42,8 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* import javax.inject.Inject import kotlin.math.min @@ -179,7 +179,8 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { val cal = Calendar.getInstance() cal.timeInMillis = overviewData.fromTime DatePickerDialog( - this, dateSetListener, + this, R.style.MaterialPickerTheme, + dateSetListener, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt index 62435f5d19b..8770925cb50 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt @@ -123,9 +123,11 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang override fun onDestroy() { super.onDestroy() - PreferenceManager - .getDefaultSharedPreferences(context) - .unregisterOnSharedPreferenceChangeListener(this) + context?.let { context -> + PreferenceManager + .getDefaultSharedPreferences(context) + .unregisterOnSharedPreferenceChangeListener(this) + } } private fun addPreferencesFromResourceIfEnabled(p: PluginBase?, rootKey: String?, enabled: Boolean) { @@ -139,9 +141,11 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - PreferenceManager - .getDefaultSharedPreferences(context) - .registerOnSharedPreferenceChangeListener(this) + context?.let { context -> + PreferenceManager + .getDefaultSharedPreferences(context) + .registerOnSharedPreferenceChangeListener(this) + } } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -263,19 +267,19 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang @SuppressLint("RestrictedApi") private fun addPreferencesFromResource(@XmlRes preferencesResId: Int, key: String?) { - val xmlRoot = preferenceManager.inflateFromResource(context, - preferencesResId, null) - val root: Preference? - if (key != null) { - root = xmlRoot.findPreference(key) - if (root == null) return - require(root is PreferenceScreen) { - ("Preference object with key " + key - + " is not a PreferenceScreen") + context?.let { context -> + val xmlRoot = preferenceManager.inflateFromResource(context, preferencesResId, null) + val root: Preference? + if (key != null) { + root = xmlRoot.findPreference(key) + if (root == null) return + require(root is PreferenceScreen) { + ("Preference object with key $key is not a PreferenceScreen") + } + preferenceScreen = root + } else { + addPreferencesFromResource(preferencesResId) } - preferenceScreen = root - } else { - addPreferencesFromResource(preferencesResId) } } @@ -305,15 +309,9 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang p.initialExpandedChildrenCount = Int.MAX_VALUE } } else { - if (p.key != null) { - visible = visible || p.key.contains(filter, true) - } - if (p.title != null) { - visible = visible || p.title.contains(filter, true) - } - if (p.summary != null) { - visible = visible || p.summary.contains(filter, true) - } + visible = visible || p.key?.contains(filter, true) == true + visible = visible || p.title?.contains(filter, true) == true + visible = visible || p.summary?.contains(filter, true) == true } p.isVisible = visible @@ -393,32 +391,30 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang // We use Preference and custom editor instead of EditTextPreference // to hash password while it is saved and never have to show it, even hashed - override fun onPreferenceTreeClick(preference: Preference?): Boolean { + override fun onPreferenceTreeClick(preference: Preference): Boolean { context?.let { context -> - if (preference != null) { - if (preference.key == rh.gs(R.string.key_master_password)) { - passwordCheck.queryPassword(context, R.string.current_master_password, R.string.key_master_password, { - passwordCheck.setPassword(context, R.string.master_password, R.string.key_master_password) - }) - return true - } - if (preference.key == rh.gs(R.string.key_settings_password)) { - passwordCheck.setPassword(context, R.string.settings_password, R.string.key_settings_password) - return true - } - if (preference.key == rh.gs(R.string.key_bolus_password)) { - passwordCheck.setPassword(context, R.string.bolus_password, R.string.key_bolus_password) - return true - } - if (preference.key == rh.gs(R.string.key_application_password)) { - passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password) - return true - } - // NSClient copy settings - if (preference.key == rh.gs(R.string.key_statuslights_copy_ns)) { - nsSettingStatus.copyStatusLightsNsSettings(context) - return true - } + if (preference.key == rh.gs(R.string.key_master_password)) { + passwordCheck.queryPassword(context, R.string.current_master_password, R.string.key_master_password, { + passwordCheck.setPassword(context, R.string.master_password, R.string.key_master_password) + }) + return true + } + if (preference.key == rh.gs(R.string.key_settings_password)) { + passwordCheck.setPassword(context, R.string.settings_password, R.string.key_settings_password) + return true + } + if (preference.key == rh.gs(R.string.key_bolus_password)) { + passwordCheck.setPassword(context, R.string.bolus_password, R.string.key_bolus_password) + return true + } + if (preference.key == rh.gs(R.string.key_application_password)) { + passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password) + return true + } + // NSClient copy settings + if (preference.key == rh.gs(R.string.key_statuslights_copy_ns)) { + nsSettingStatus.copyStatusLightsNsSettings(context) + return true } } return super.onPreferenceTreeClick(preference) 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 25fc86cf2e5..8de1ff39d16 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt @@ -283,7 +283,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { tabSelected = tab typeSelected[tabSelected] = newContent - binding.profiletypeTitle.defaultHintTextColor = ColorStateList.valueOf(rh.gc(if (tab == 0) R.color.tabBgColorSelected else R.color.examinedProfile)) + binding.profiletypeTitle.defaultHintTextColor = ColorStateList.valueOf(rh.gc(if (tab == 0) R.color.helperProfile else R.color.examinedProfile)) // show new content binding.profiletype.setText( @@ -314,7 +314,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } private fun setBackgroundColorOnSelected(tab: Int) { - binding.menu1.setBackgroundColor(rh.gc(if (tab == 1) R.color.defaultbackground else R.color.tempbasal)) + binding.menu1.setBackgroundColor(rh.gc(if (tab == 1) R.color.defaultbackground else R.color.helperProfile)) binding.menu2.setBackgroundColor(rh.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile)) } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/TreatmentsActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/TreatmentsActivity.kt index 9a68aeaf5ff..5290b48711d 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/TreatmentsActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/TreatmentsActivity.kt @@ -1,13 +1,13 @@ package info.nightscout.androidaps.activities import android.os.Bundle +import android.view.MenuItem import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.fragments.* import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding -import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.utils.buildHelper.BuildHelper import javax.inject.Inject @@ -23,40 +23,60 @@ class TreatmentsActivity : NoSplashAppCompatActivity() { super.onCreate(savedInstanceState) binding = TreatmentsFragmentBinding.inflate(layoutInflater) setContentView(binding.root) - //binding.tempBasals.visibility = buildHelper.isEngineeringMode().toVisibility() //binding.extendedBoluses.visibility = (buildHelper.isEngineeringMode() && !activePlugin.activePump.isFakingTempsByExtendedBoluses).toVisibility() binding.treatments.setOnClickListener { setFragment(TreatmentsBolusCarbsFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.carbs_and_bolus) } binding.extendedBoluses.setOnClickListener { setFragment(TreatmentsExtendedBolusesFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.extended_bolus) } binding.tempBasals.setOnClickListener { setFragment(TreatmentsTemporaryBasalsFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.tempbasal_label) } binding.tempTargets.setOnClickListener { setFragment(TreatmentsTempTargetFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.tempt_targets) } binding.profileSwitches.setOnClickListener { setFragment(TreatmentsProfileSwitchFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.profile_changes) } binding.careportal.setOnClickListener { setFragment(TreatmentsCareportalFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.careportal) } binding.userentry.setOnClickListener { setFragment(TreatmentsUserEntryFragment()) setBackgroundColorOnSelected(it) + supportActionBar?.title = rh.gs(R.string.user_action) } setFragment(TreatmentsBolusCarbsFragment()) setBackgroundColorOnSelected(binding.treatments) + setSupportActionBar(binding.toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.title = rh.gs(R.string.carbs_and_bolus) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + android.R.id.home -> { + finish() + true + } + + else -> false + } } private fun setFragment(selectedFragment: Fragment) { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt index 3ed805966ea..1c569a78a6e 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt @@ -3,9 +3,13 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.util.Log +import android.util.SparseArray +import android.view.* +import android.widget.CompoundButton +import android.view.ActionMode +import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -45,10 +49,10 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.Completable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy +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 java.util.concurrent.TimeUnit import javax.inject.Inject @@ -67,6 +71,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository @Inject lateinit var activePlugin: ActivePlugin + private var _binding: TreatmentsBolusCarbsFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. + private val binding get() = _binding!! + class MealLink( val bolus: Bolus? = null, val carbs: Carbs? = null, @@ -74,14 +82,12 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { ) private val disposable = CompositeDisposable() - private val millsToThePast = T.days(30).msecs() - private var _binding: TreatmentsBolusCarbsFragmentBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! + private var selectedItems: SparseArray = SparseArray() + private var showInvalidated = false + private var removeActionMode: ActionMode? = null + private var toolbar: Toolbar? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root @@ -89,92 +95,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { @SuppressLint("CheckResult") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) + toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - - binding.refreshFromNightscout.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") { - uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) - disposable += - Completable.fromAction { - repository.deleteAllBolusCalculatorResults() - repository.deleteAllBoluses() - repository.deleteAllCarbs() - } - .subscribeOn(aapsSchedulers.io) - .observeOn(aapsSchedulers.main) - .subscribeBy( - onError = { aapsLogger.error("Error removing entries", it) }, - onComplete = { - rxBus.send(EventTreatmentChange()) - rxBus.send(EventNewHistoryData(0, false)) - } - ) - rxBus.send(EventNSClientRestart()) - } - } - } - binding.deleteFutureTreatments.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), rh.gs(R.string.deletefuturetreatments) + "?", Runnable { - uel.log(Action.DELETE_FUTURE_TREATMENTS, Sources.Treatments) - repository - .getBolusesDataFromTime(dateUtil.now(), false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> - list.forEach { bolus -> - disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id)) - .subscribe( - { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) } - ) - } - } - repository - .getCarbsDataFromTimeNotExpanded(dateUtil.now(), false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> - list.forEach { carb -> - if (carb.duration == 0L) - disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id)) - .subscribe( - { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } - ) - else { - disposable += repository.runTransactionForResult(CutCarbsTransaction(carb.id, dateUtil.now())) - .subscribe( - { result -> - result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated (cut end) carbs $it") } - }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } - ) - } - } - } - repository - .getBolusCalculatorResultsDataFromTime(dateUtil.now(), false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> - list.forEach { bolusCalc -> - disposable += repository.runTransactionForResult(InvalidateBolusCalculatorResultTransaction(bolusCalc.id)) - .subscribe( - { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolusCalculatorResult $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolusCalculatorResult", it) } - ) - } - } - binding.deleteFutureTreatments.visibility = View.GONE - }) - } - } - val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode() - if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE - binding.showInvalidated.setOnCheckedChangeListener { _, _ -> - rxBus.send(EventTreatmentUpdateGui()) - } } private fun bolusMealLinksWithInvalid(now: Long) = repository @@ -204,36 +128,35 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) - disposable += carbsMealLinksWithInvalid(now) - .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } - .zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second } - .map { ml -> - ml.sortedByDescending { - it.carbs?.timestamp ?: it.bolus?.timestamp - ?: it.bolusCalculatorResult?.timestamp + disposable += + if (showInvalidated) + carbsMealLinksWithInvalid(now) + .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } + .zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second } + .map { ml -> + ml.sortedByDescending { + it.carbs?.timestamp ?: it.bolus?.timestamp + ?: it.bolusCalculatorResult?.timestamp + } } - } - .observeOn(aapsSchedulers.main) - .subscribe { list -> - binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) - binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility() - } - else - disposable += carbsMealLinks(now) - .zipWith(bolusMealLinks(now)) { first, second -> first + second } - .zipWith(calcResultMealLinks(now)) { first, second -> first + second } - .map { ml -> - ml.sortedByDescending { - it.carbs?.timestamp ?: it.bolus?.timestamp - ?: it.bolusCalculatorResult?.timestamp + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) + } + else + carbsMealLinks(now) + .zipWith(bolusMealLinks(now)) { first, second -> first + second } + .zipWith(calcResultMealLinks(now)) { first, second -> first + second } + .map { ml -> + ml.sortedByDescending { + it.carbs?.timestamp ?: it.bolus?.timestamp + ?: it.bolusCalculatorResult?.timestamp + } + } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } - } - .observeOn(aapsSchedulers.main) - .subscribe { list -> - binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) - binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility() - } } @@ -267,13 +190,14 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } - private fun timestamp(ml: MealLink): Long = ml.bolusCalculatorResult?.let { it.timestamp } ?: ml.bolus?.let { it.timestamp } ?: ml.carbs?.let { it.timestamp } ?: 0L + private fun timestamp(ml: MealLink): Long = ml.bolusCalculatorResult?.timestamp ?: ml.bolus?.timestamp ?: ml.carbs?.timestamp ?: 0L - inner class RecyclerViewAdapter internal constructor(var mealLinks: List) : RecyclerView.Adapter() { + inner class RecyclerViewAdapter internal constructor(private var mealLinks: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MealLinkLoadedViewHolder = MealLinkLoadedViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_bolus_carbs_item, viewGroup, false)) @@ -287,13 +211,13 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { holder.binding.date.text = dateUtil.dateString(timestamp(ml)) // Metadata - holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || binding.showInvalidated.isChecked)).toVisibility() + holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || showInvalidated)).toVisibility() ml.bolusCalculatorResult?.let { bolusCalculatorResult -> holder.binding.calcTime.text = dateUtil.timeString(bolusCalculatorResult.timestamp) } // Bolus - holder.binding.bolusLayout.visibility = (ml.bolus != null && (ml.bolus.isValid || binding.showInvalidated.isChecked)).toVisibility() + holder.binding.bolusLayout.visibility = (ml.bolus != null && (ml.bolus.isValid || showInvalidated)).toVisibility() ml.bolus?.let { bolus -> holder.binding.bolusTime.text = dateUtil.timeString(bolus.timestamp) holder.binding.insulin.text = rh.gs(R.string.formatinsulinunits, bolus.amount) @@ -321,7 +245,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } } // Carbs - holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || binding.showInvalidated.isChecked)).toVisibility() + holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || showInvalidated)).toVisibility() ml.carbs?.let { carbs -> holder.binding.carbsTime.text = dateUtil.timeString(carbs.timestamp) holder.binding.carbs.text = rh.gs(R.string.format_carbs, carbs.amount.toInt()) @@ -330,19 +254,28 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility() holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility() } - - holder.binding.bolusRemove.visibility = (ml.bolus?.isValid == true).toVisibility() - holder.binding.carbsRemove.visibility = (ml.carbs?.isValid == true).toVisibility() - holder.binding.bolusRemove.tag = ml - holder.binding.carbsRemove.tag = ml + holder.binding.cbBolusRemove.visibility = (ml.bolus?.isValid == true && removeActionMode != null).toVisibility() + holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && removeActionMode != null).toVisibility() + if (removeActionMode != null) { + val onChange = CompoundButton.OnCheckedChangeListener { _, value -> + if (value) { + selectedItems.put(position, ml) + } else { + selectedItems.remove(position) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + holder.binding.cbBolusRemove.setOnCheckedChangeListener(onChange) + holder.binding.cbBolusRemove.isChecked = selectedItems.get(position) != null + holder.binding.cbCarbsRemove.setOnCheckedChangeListener(onChange) + holder.binding.cbCarbsRemove.isChecked = selectedItems.get(position) != null + } holder.binding.calculation.tag = ml val nextTimestamp = if (mealLinks.size != position + 1) timestamp(mealLinks[position + 1]) else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(timestamp(ml), nextTimestamp).toVisibility() } - override fun getItemCount(): Int { - return mealLinks.size - } + override fun getItemCount() = mealLinks.size inner class MealLinkLoadedViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) { @@ -359,35 +292,199 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { } } binding.calculation.paintFlags = binding.calculation.paintFlags or Paint.UNDERLINE_TEXT_FLAG - binding.bolusRemove.setOnClickListener { ml -> - val bolus = (ml.tag as MealLink?)?.bolus ?: return@setOnClickListener - activity?.let { activity -> - val text = rh.gs(R.string.configbuilder_insulin) + ": " + - rh.gs(R.string.formatinsulinunits, bolus.amount) + "\n" + - rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(bolus.timestamp) - OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable { + } + } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_treatments_carbs_bolus, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode() + menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly + val hasItems = (binding.recyclerview.adapter?.itemCount ?: 0) > 0 + menu.findItem(R.id.nav_delete_future)?.isVisible = hasItems + + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { + R.id.nav_remove_items -> { + removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) + true + } + + R.id.nav_show_invalidated -> { + showInvalidated = true + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_hide_invalidated -> { + showInvalidated = false + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_delete_future -> { + deleteFutureTreatments() + true + } + + R.id.nav_refresh_ns -> { + refreshFromNightscout() + true + } + + else -> false + } + + private fun refreshFromNightscout() { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") { + uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) + disposable += + Completable.fromAction { + repository.deleteAllBolusCalculatorResults() + repository.deleteAllBoluses() + repository.deleteAllCarbs() + } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribeBy( + onError = { aapsLogger.error("Error removing entries", it) }, + onComplete = { + rxBus.send(EventTreatmentChange()) + rxBus.send(EventNewHistoryData(0, false)) + } + ) + rxBus.send(EventNSClientRestart()) + } + } + } + + fun deleteFutureTreatments() { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), rh.gs(R.string.deletefuturetreatments) + "?", Runnable { + uel.log(Action.DELETE_FUTURE_TREATMENTS, Sources.Treatments) + disposable += repository + .getBolusesDataFromTime(dateUtil.now(), false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> + list.forEach { bolus -> + disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) } + ) + } + } + disposable += repository + .getCarbsDataFromTimeNotExpanded(dateUtil.now(), false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> + list.forEach { carb -> + if (carb.duration == 0L) + disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } + ) + else { + disposable += repository.runTransactionForResult(CutCarbsTransaction(carb.id, dateUtil.now())) + .subscribe( + { result -> + result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated (cut end) carbs $it") } + }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } + ) + } + } + } + disposable += repository + .getBolusCalculatorResultsDataFromTime(dateUtil.now(), false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> + list.forEach { bolusCalc -> + disposable += repository.runTransactionForResult(InvalidateBolusCalculatorResultTransaction(bolusCalc.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolusCalculatorResult $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolusCalculatorResult", it) } + ) + } + } + }) + } + } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.recyclerview.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true + } + + else -> false + } + } + + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val mealLink = selectedItems.valueAt(0) + val bolus = mealLink.bolus + if (bolus != null) + return rh.gs(R.string.configbuilder_insulin) + ": " + rh.gs(R.string.formatinsulinunits, bolus.amount) + "\n" + + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(bolus.timestamp) + val carbs = mealLink.carbs + if (carbs != null) + return rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carbs.amount.toInt()) + "\n" + + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(carbs.timestamp) + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) + } + + fun removeSelected() { + if (selectedItems.size() > 0) + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach { _, ml -> + ml.bolus?.let { bolus -> uel.log( Action.BOLUS_REMOVED, Sources.Treatments, ValueWithUnit.Timestamp(bolus.timestamp), ValueWithUnit.Insulin(bolus.amount) - //ValueWithUnit.Gram(mealLinkLoaded.carbs.toInt()) ) disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id)) .subscribe( { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } }, { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) } ) - }) - } - } - binding.bolusRemove.paintFlags = binding.bolusRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG - binding.carbsRemove.setOnClickListener { ml -> - val carb = (ml.tag as MealLink?)?.carbs ?: return@setOnClickListener - activity?.let { activity -> - val text = rh.gs(R.string.carbs) + ": " + - rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carb.amount.toInt()) + "\n" + - rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(carb.timestamp) - OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable { + } + ml.carbs?.let { carb -> uel.log( Action.CARBS_REMOVED, Sources.Treatments, ValueWithUnit.Timestamp(carb.timestamp), @@ -398,11 +495,12 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } }, { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } ) - }) + } } - } - binding.carbsRemove.paintFlags = binding.carbsRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + removeActionMode?.finish() + }) } - } + else + removeActionMode?.finish() } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt index 0dca0e416f2..6e0bfcf4a79 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt @@ -1,10 +1,11 @@ package info.nightscout.androidaps.activities.fragments -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.util.SparseArray +import android.view.* +import android.view.ActionMode +import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -36,10 +37,10 @@ import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.Completable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy +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 java.util.concurrent.TimeUnit import javax.inject.Inject @@ -57,61 +58,61 @@ class TreatmentsCareportalFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository @Inject lateinit var uel: UserEntryLogger - private val disposable = CompositeDisposable() + private var _binding: TreatmentsCareportalFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. + private val binding get() = _binding!! + private val disposable = CompositeDisposable() private val millsToThePast = T.days(30).msecs() + private var selectedItems: SparseArray = SparseArray() + private var showInvalidated = false + private var toolbar: Toolbar? = null + private var removeActionMode: ActionMode? = null - private var _binding: TreatmentsCareportalFragmentBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + toolbar = activity?.findViewById(R.id.toolbar) + setHasOptionsMenu(true) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.refreshFromNightscout.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.refresheventsfromnightscout) + " ?", Runnable { - uel.log(Action.CAREPORTAL_NS_REFRESH, Sources.Treatments) - disposable += Completable.fromAction { repository.deleteAllTherapyEventsEntries() } - .subscribeOn(aapsSchedulers.io) - .subscribeBy( - onError = { aapsLogger.error("Error removing entries", it) }, - onComplete = { rxBus.send(EventTherapyEventChange()) } - ) - rxBus.send(EventNSClientRestart()) - }) - } - } - binding.removeAndroidapsStartedEvents.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.careportal_removestartedevents), Runnable { - uel.log(Action.RESTART_EVENTS_REMOVED, Sources.Treatments) - repository.runTransactionForResult(InvalidateAAPSStartedTherapyEventTransaction(rh.gs(R.string.androidaps_start))) - .subscribe( - { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } - ) - }, null) - } + } + + private fun refreshFromNightscout() { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.refresheventsfromnightscout) + " ?", Runnable { + uel.log(Action.CAREPORTAL_NS_REFRESH, Sources.Treatments) + disposable += Completable.fromAction { repository.deleteAllTherapyEventsEntries() } + .subscribeOn(aapsSchedulers.io) + .subscribeBy( + onError = { aapsLogger.error("Error removing entries", it) }, + onComplete = { rxBus.send(EventTherapyEventChange()) } + ) + rxBus.send(EventNSClientRestart()) + }) } + } - val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode() - if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE - binding.showInvalidated.setOnCheckedChangeListener { _, _ -> - rxBus.send(EventTreatmentUpdateGui()) + private fun removeStartedEvents() { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.careportal), rh.gs(R.string.careportal_removestartedevents), Runnable { + uel.log(Action.RESTART_EVENTS_REMOVED, Sources.Treatments) + disposable += repository.runTransactionForResult(InvalidateAAPSStartedTherapyEventTransaction(rh.gs(R.string.androidaps_start))) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } + ) + }) } } fun swapAdapter() { val now = System.currentTimeMillis() disposable += - if (binding.showInvalidated.isChecked) + if (showInvalidated) repository .getTherapyEventDataIncludingInvalidFromTime(now - millsToThePast, false) .observeOn(aapsSchedulers.main) @@ -148,6 +149,7 @@ class TreatmentsCareportalFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -170,42 +172,137 @@ class TreatmentsCareportalFragment : DaggerFragment() { holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh) holder.binding.note.text = therapyEvent.note holder.binding.type.text = translator.translate(therapyEvent.type) - holder.binding.remove.tag = therapyEvent + holder.binding.cbRemove.visibility = (therapyEvent.isValid && removeActionMode != null).toVisibility() + if (removeActionMode != null) { + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.put(position, therapyEvent) + } else { + selectedItems.remove(position) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + } val nextTimestamp = if (therapyList.size != position + 1) therapyList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(therapyEvent.timestamp, nextTimestamp).toVisibility() } - override fun getItemCount(): Int { - return therapyList.size - } + override fun getItemCount() = therapyList.size inner class TherapyEventsViewHolder(view: View) : RecyclerView.ViewHolder(view) { val binding = TreatmentsCareportalItemBinding.bind(view) + } - init { - binding.remove.setOnClickListener { v: View -> - val therapyEvent = v.tag as TherapyEvent - activity?.let { activity -> - val text = rh.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" + - rh.gs(R.string.notes_label) + ": " + (therapyEvent.note - ?: "") + "\n" + - rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(therapyEvent.timestamp) - OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable { - uel.log(Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note , - ValueWithUnit.Timestamp(therapyEvent.timestamp), - ValueWithUnit.TherapyEventType(therapyEvent.type)) - disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id)) - .subscribe( - { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } - ) - }, null) - } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_treatments_careportal, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode() + menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly + + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { + R.id.nav_remove_items -> { + removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) + true + } + + R.id.nav_show_invalidated -> { + showInvalidated = true + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_hide_invalidated -> { + showInvalidated = false + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_remove_started_events -> { + removeStartedEvents() + true + } + + R.id.nav_refresh_ns -> { + refreshFromNightscout() + true + } + + else -> false + } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.recyclerview.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true } - binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + + else -> false } } + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val therapyEvent = selectedItems.valueAt(0) + return rh.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" + + rh.gs(R.string.notes_label) + ": " + (therapyEvent.note ?: "") + "\n" + + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(therapyEvent.timestamp) + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } -} \ No newline at end of file + + private fun removeSelected() { + if (selectedItems.size() > 0) + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach { _, therapyEvent -> + uel.log( + Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note, + ValueWithUnit.Timestamp(therapyEvent.timestamp), + ValueWithUnit.TherapyEventType(therapyEvent.type) + ) + disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } + ) + } + removeActionMode?.finish() + }) + } + else + removeActionMode?.finish() + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt index bdd0e4f4246..e1d8d4b34d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt @@ -1,12 +1,12 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint -import android.content.DialogInterface -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.util.SparseArray +import android.view.* +import android.view.ActionMode +import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -27,18 +27,19 @@ import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment.RecyclerViewAdapter.ExtendedBolusesViewHolder +import info.nightscout.androidaps.events.EventTreatmentUpdateGui import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import info.nightscout.shared.logging.LTag import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -60,23 +61,27 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository private var _binding: TreatmentsExtendedbolusFragmentBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View = + private var selectedItems: SparseArray = SparseArray() + private var showInvalidated = false + private var removeActionMode: ActionMode? = null + private var toolbar: Toolbar? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + setHasOptionsMenu(true) + toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) } fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) + disposable += if (showInvalidated) repository .getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false) .observeOn(aapsSchedulers.main) @@ -109,6 +114,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -125,7 +131,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).toVisibility() holder.binding.invalid.visibility = extendedBolus.isValid.not().toVisibility() - val sameDayPrevious = position > 0 && dateUtil.isSameDay(extendedBolus.timestamp, extendedBolusList[position-1].timestamp) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(extendedBolus.timestamp, extendedBolusList[position - 1].timestamp) holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.text = dateUtil.dateString(extendedBolus.timestamp) @SuppressLint("SetTextI18n") @@ -143,41 +149,125 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iob) holder.binding.ratio.text = rh.gs(R.string.pump_basebasalrate, extendedBolus.rate) if (iob.iob != 0.0) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor) - holder.binding.remove.tag = extendedBolus + holder.binding.cbRemove.visibility = (extendedBolus.isValid && removeActionMode != null).toVisibility() + if (removeActionMode != null) { + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.put(position, extendedBolus) + } else { + selectedItems.remove(position) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + } val nextTimestamp = if (extendedBolusList.size != position + 1) extendedBolusList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(extendedBolus.timestamp, nextTimestamp).toVisibility() } - override fun getItemCount(): Int = extendedBolusList.size + override fun getItemCount() = extendedBolusList.size inner class ExtendedBolusesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val binding = TreatmentsExtendedbolusItemBinding.bind(itemView) + } - init { - binding.remove.setOnClickListener { v: View -> - val extendedBolus = v.tag as ExtendedBolus - context?.let { context -> - OKDialog.showConfirmation(context, rh.gs(R.string.removerecord), - """ - ${rh.gs(R.string.extended_bolus)} - ${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(extendedBolus.timestamp)} - """.trimIndent(), { _: DialogInterface, _: Int -> - uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments, - ValueWithUnit.Timestamp(extendedBolus.timestamp), - ValueWithUnit.Insulin(extendedBolus.amount), - ValueWithUnit.UnitPerHour(extendedBolus.rate), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt())) - disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id)) - .subscribe( - { aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) }) - }, null) - } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_treatments_extended_bolus, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.nav_remove_items -> { + removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) + true + } + + R.id.nav_show_invalidated -> { + showInvalidated = true + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_hide_invalidated -> { + showInvalidated = false + rxBus.send(EventTreatmentUpdateGui()) + true + } + + else -> false + } + } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.recyclerview.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true } - binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + + else -> false } } + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val bolus = selectedItems.valueAt(0) + return rh.gs(R.string.extended_bolus) + "\n" + + "${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(bolus.timestamp)}" + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) + } + + private fun removeSelected() { + if (selectedItems.size() > 0) + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach { _, extendedBolus -> + uel.log( + Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(extendedBolus.timestamp), + ValueWithUnit.Insulin(extendedBolus.amount), + ValueWithUnit.UnitPerHour(extendedBolus.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt()) + ) + disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id)) + .subscribe( + { aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) }) + } + removeActionMode?.finish() + }) + } + else + removeActionMode?.finish() } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt index 91b46228be6..08d4e202402 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt @@ -2,13 +2,16 @@ package info.nightscout.androidaps.activities.fragments import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.util.SparseArray +import android.view.* +import android.view.ActionMode +import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.UserEntry.Action @@ -18,20 +21,17 @@ import info.nightscout.androidaps.database.transactions.InvalidateProfileSwitchT import info.nightscout.androidaps.databinding.TreatmentsProfileswitchFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsProfileswitchItemBinding import info.nightscout.androidaps.dialogs.ProfileViewerDialog +import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged import info.nightscout.androidaps.events.EventProfileSwitchChanged +import info.nightscout.androidaps.events.EventTreatmentUpdateGui import info.nightscout.androidaps.extensions.getCustomizedName import info.nightscout.androidaps.extensions.toVisibility -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged -import info.nightscout.androidaps.events.EventTreatmentUpdateGui -import info.nightscout.androidaps.activities.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder -import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T @@ -39,11 +39,13 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.Completable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy +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 class TreatmentsProfileSwitchFragment : DaggerFragment() { @@ -61,50 +63,50 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { @Inject lateinit var uel: UserEntryLogger private var _binding: TreatmentsProfileswitchFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. + private val binding get() = _binding!! private val disposable = CompositeDisposable() - private val millsToThePast = T.days(30).msecs() + private var selectedItems: SparseArray = SparseArray() + private var showInvalidated = false + private var removeActionMode: ActionMode? = null + private var toolbar: Toolbar? = null - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) + toolbar = activity?.findViewById(R.id.toolbar) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) + } - binding.refreshFromNightscout.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") { - uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) - disposable += - Completable.fromAction { - repository.deleteAllEffectiveProfileSwitches() - repository.deleteAllProfileSwitches() - } - .subscribeOn(aapsSchedulers.io) - .observeOn(aapsSchedulers.main) - .subscribeBy( - onError = { aapsLogger.error("Error removing entries", it) }, - onComplete = { - rxBus.send(EventProfileSwitchChanged()) - rxBus.send(EventEffectiveProfileSwitchChanged(0L)) - rxBus.send(EventNewHistoryData(0, false)) - } - ) - rxBus.send(EventNSClientRestart()) - } + private fun refreshFromNightscout() { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") { + uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) + disposable += + Completable.fromAction { + repository.deleteAllEffectiveProfileSwitches() + repository.deleteAllProfileSwitches() + } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribeBy( + onError = { aapsLogger.error("Error removing entries", it) }, + onComplete = { + rxBus.send(EventProfileSwitchChanged()) + rxBus.send(EventEffectiveProfileSwitchChanged(0L)) + rxBus.send(EventNewHistoryData(0, false)) + } + ) + rxBus.send(EventNSClientRestart()) } } - if (!sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode()) binding.refreshFromNightscout.visibility = View.GONE - binding.showInvalidated.setOnCheckedChangeListener { _, _ -> - rxBus.send(EventTreatmentUpdateGui()) - } } private fun profileSwitchWithInvalid(now: Long) = repository @@ -126,23 +128,23 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) - disposable += profileSwitchWithInvalid(now) - .zipWith(effectiveProfileSwitchWithInvalid(now)) { first, second -> first + second } - .map { ml -> ml.sortedByDescending { it.timestamp } } - .observeOn(aapsSchedulers.main) - .subscribe { list -> - binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) - } - else - disposable += profileSwitches(now) - .zipWith(effectiveProfileSwitches(now)) { first, second -> first + second } - .map { ml -> ml.sortedByDescending { it.timestamp } } - .observeOn(aapsSchedulers.main) - .subscribe { list -> - binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) - } - + disposable += + if (showInvalidated) + profileSwitchWithInvalid(now) + .zipWith(effectiveProfileSwitchWithInvalid(now)) { first, second -> first + second } + .map { ml -> ml.sortedByDescending { it.timestamp } } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) + } + else + profileSwitches(now) + .zipWith(effectiveProfileSwitches(now)) { first, second -> first + second } + .map { ml -> ml.sortedByDescending { it.timestamp } } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) + } } @Synchronized @@ -168,6 +170,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.finish() binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -186,64 +189,66 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { holder.binding.date.text = dateUtil.dateString(profileSwitch.timestamp) holder.binding.time.text = dateUtil.timeString(profileSwitch.timestamp) holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(profileSwitch.duration ?: 0L).mins()) - holder.binding.name.text = if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName() else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else "" + holder.binding.name.text = + if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName() else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else "" if (profileSwitch.isInProgress(dateUtil)) holder.binding.date.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) - holder.binding.remove.tag = profileSwitch holder.binding.clone.tag = profileSwitch holder.binding.name.tag = profileSwitch holder.binding.date.tag = profileSwitch holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility() holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility() - holder.binding.remove.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() + holder.binding.cbRemove.visibility = (removeActionMode != null && profileSwitch is ProfileSealed.PS).toVisibility() + if (removeActionMode != null) { + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.put(position, profileSwitch) + } else { + selectedItems.remove(position) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + } holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() - holder.binding.root.setBackgroundColor(rh.gc(if (profileSwitch is ProfileSealed.PS) R.color.defaultbackground else R.color.list_delimiter)) val nextTimestamp = if (profileSwitchList.size != position + 1) profileSwitchList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(profileSwitch.timestamp, nextTimestamp).toVisibility() } - override fun getItemCount(): Int { - return profileSwitchList.size - } + override fun getItemCount() = profileSwitchList.size inner class ProfileSwitchViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { val binding = TreatmentsProfileswitchItemBinding.bind(itemView) init { - binding.remove.setOnClickListener { view -> - val profileSwitch = view.tag as ProfileSealed.PS - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), - rh.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName + - "\n" + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp), Runnable { - uel.log(Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName, - ValueWithUnit.Timestamp(profileSwitch.timestamp)) - disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id)) - .subscribe( - { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) } - ) - }) - } - } binding.clone.setOnClickListener { activity?.let { activity -> val profileSwitch = (it.tag as ProfileSealed.PS).value val profileSealed = it.tag as ProfileSealed - OKDialog.showConfirmation(activity, rh.gs(R.string.careportal_profileswitch), rh.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.getCustomizedName() + "\n" + dateUtil.dateAndTimeString(profileSwitch.timestamp), Runnable { - uel.log(Action.PROFILE_SWITCH_CLONED, Sources.Treatments, - profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"), - ValueWithUnit.Timestamp(profileSwitch.timestamp), - ValueWithUnit.SimpleString(profileSwitch.profileName)) - val nonCustomized = profileSealed.convertToNonCustomizedProfile(dateUtil) - localProfilePlugin.addProfile(localProfilePlugin.copyFrom(nonCustomized, profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"))) - rxBus.send(EventLocalProfileChanged()) - }) + OKDialog.showConfirmation( + activity, + rh.gs(R.string.careportal_profileswitch), + rh.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.getCustomizedName() + "\n" + dateUtil.dateAndTimeString(profileSwitch.timestamp), + Runnable { + uel.log( + Action.PROFILE_SWITCH_CLONED, Sources.Treatments, + profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"), + ValueWithUnit.Timestamp(profileSwitch.timestamp), + ValueWithUnit.SimpleString(profileSwitch.profileName) + ) + val nonCustomized = profileSealed.convertToNonCustomizedProfile(dateUtil) + localProfilePlugin.addProfile( + localProfilePlugin.copyFrom( + nonCustomized, + profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_") + ) + ) + rxBus.send(EventLocalProfileChanged()) + }) } } - binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG binding.clone.paintFlags = binding.clone.paintFlags or Paint.UNDERLINE_TEXT_FLAG binding.name.setOnClickListener { ProfileViewerDialog().also { pvd -> @@ -266,4 +271,104 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { } } } -} \ No newline at end of file + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_treatments_profile_switch, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode() + menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly + + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { + R.id.nav_remove_items -> { + removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) + true + } + + R.id.nav_show_invalidated -> { + showInvalidated = true + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_hide_invalidated -> { + showInvalidated = false + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_refresh_ns -> { + refreshFromNightscout() + true + } + + else -> false + } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.recyclerview.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true + } + + else -> false + } + } + + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val profileSwitch = selectedItems.valueAt(0) + return rh.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName + "\n" + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp) + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) + } + + private fun removeSelected() { + if (selectedItems.size() > 0) + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach { _, profileSwitch -> + uel.log( + Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName, + ValueWithUnit.Timestamp(profileSwitch.timestamp) + ) + disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) } + ) + } + removeActionMode?.finish() + }) + } + else + removeActionMode?.finish() + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt index c408fd42997..777a61a932e 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt @@ -1,12 +1,11 @@ package info.nightscout.androidaps.activities.fragments import android.annotation.SuppressLint -import android.content.DialogInterface -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.util.SparseArray +import android.view.* +import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -30,6 +29,8 @@ import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.events.EventTreatmentUpdateGui import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder +import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged +import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T @@ -40,13 +41,14 @@ import info.nightscout.androidaps.extensions.friendlyDescription import info.nightscout.androidaps.extensions.highValueToUnitsToString import info.nightscout.androidaps.extensions.lowValueToUnitsToString import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.Completable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy +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 java.util.concurrent.TimeUnit import javax.inject.Inject @@ -65,57 +67,64 @@ class TreatmentsTempTargetFragment : DaggerFragment() { @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository - private val disposable = CompositeDisposable() - - private val millsToThePast = T.days(30).msecs() - private var _binding: TreatmentsTemptargetFragmentBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! + private val disposable = CompositeDisposable() + private val millsToThePast = T.days(30).msecs() + private var selectedItems: SparseArray = SparseArray() + private var showInvalidated = false + private var toolbar: Toolbar? = null + private var removeActionMode: ActionMode? = null + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.recyclerview.setHasFixedSize(true) + toolbar = activity?.findViewById(R.id.toolbar) + setHasOptionsMenu(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.refreshFromNightscout.setOnClickListener { - context?.let { context -> - OKDialog.showConfirmation(context, rh.gs(R.string.refresheventsfromnightscout) + " ?", { - uel.log(Action.TT_NS_REFRESH, Sources.Treatments) - disposable += Completable.fromAction { repository.deleteAllTempTargetEntries() } + } + + private fun refreshFromNightscout() { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.refresheventsfromnightscout) + "?") { + uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) + disposable += + Completable.fromAction { + repository.deleteAllEffectiveProfileSwitches() + repository.deleteAllProfileSwitches() + } .subscribeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.main) .subscribeBy( onError = { aapsLogger.error("Error removing entries", it) }, - onComplete = { rxBus.send(EventTempTargetChange()) } + onComplete = { + rxBus.send(EventProfileSwitchChanged()) + rxBus.send(EventEffectiveProfileSwitchChanged(0L)) + rxBus.send(EventNewHistoryData(0, false)) + } ) - - rxBus.send(EventNSClientRestart()) - }) + rxBus.send(EventNSClientRestart()) } } - val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode() - if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.INVISIBLE - binding.showInvalidated.setOnCheckedChangeListener { _, _ -> - rxBus.send(EventTreatmentUpdateGui()) - } } fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) - repository - .getTemporaryTargetDataIncludingInvalidFromTime(now - millsToThePast, false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } - else - repository - .getTemporaryTargetDataFromTime(now - millsToThePast, false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + disposable += + if (showInvalidated) + repository + .getTemporaryTargetDataIncludingInvalidFromTime(now - millsToThePast, false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + else + repository + .getTemporaryTargetDataFromTime(now - millsToThePast, false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } } @Synchronized @@ -145,6 +154,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -163,8 +173,19 @@ class TreatmentsTempTargetFragment : DaggerFragment() { val tempTarget = tempTargetList[position] holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility() - holder.binding.remove.visibility = tempTarget.isValid.toVisibility() - val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempTarget.timestamp, tempTargetList[position-1].timestamp) + holder.binding.cbRemove.visibility = (tempTarget.isValid && removeActionMode != null).toVisibility() + if (removeActionMode != null) { + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.put(position, tempTarget) + } else { + selectedItems.remove(position) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + } + val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempTarget.timestamp, tempTargetList[position - 1].timestamp) holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.text = dateUtil.dateString(tempTarget.timestamp) holder.binding.time.text = dateUtil.timeRangeString(tempTarget.timestamp, tempTarget.end) @@ -177,43 +198,123 @@ class TreatmentsTempTargetFragment : DaggerFragment() { tempTarget.id == currentlyActiveTarget?.id -> rh.gc(R.color.colorActive) tempTarget.timestamp > dateUtil.now() -> rh.gc(R.color.colorScheduled) else -> holder.binding.reasonColon.currentTextColor - }) - holder.binding.remove.tag = tempTarget + } + ) val nextTimestamp = if (tempTargetList.size != position + 1) tempTargetList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(tempTarget.timestamp, nextTimestamp).toVisibility() } - override fun getItemCount(): Int = tempTargetList.size + override fun getItemCount() = tempTargetList.size inner class TempTargetsViewHolder(view: View) : RecyclerView.ViewHolder(view) { val binding = TreatmentsTemptargetItemBinding.bind(view) - init { - binding.remove.setOnClickListener { v: View -> - val tempTarget = v.tag as TemporaryTarget - context?.let { context -> - OKDialog.showConfirmation(context, rh.gs(R.string.removerecord), - """ - ${rh.gs(R.string.careportal_temporarytarget)}: ${tempTarget.friendlyDescription(profileFunction.getUnits(), rh)} - ${dateUtil.dateAndTimeString(tempTarget.timestamp)} - """.trimIndent(), - { _: DialogInterface?, _: Int -> - uel.log(Action.TT_REMOVED, Sources.Treatments, - ValueWithUnit.Timestamp(tempTarget.timestamp), - ValueWithUnit.TherapyEventTTReason(tempTarget.reason), - ValueWithUnit.Mgdl(tempTarget.lowTarget), - ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget }, - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt())) - disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id)) - .subscribe( - { aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) }) - }, null) + } + } + + private fun removeSelected() { + if (selectedItems.size() > 0) + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach { _, tempTarget -> + uel.log( + Action.TT_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(tempTarget.timestamp), + ValueWithUnit.TherapyEventTTReason(tempTarget.reason), + ValueWithUnit.Mgdl(tempTarget.lowTarget), + ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt()) + ) + disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id)) + .subscribe( + { aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) }) } + removeActionMode?.finish() + }) + } + else + removeActionMode?.finish() + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_treatments_temp_target, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode() + menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly + + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { + R.id.nav_remove_items -> { + removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) + true + } + + R.id.nav_show_invalidated -> { + showInvalidated = true + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_hide_invalidated -> { + showInvalidated = false + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_refresh_ns -> { + refreshFromNightscout() + true + } + + else -> false + } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.recyclerview.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true } - binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + + else -> false } } + + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } } -} \ No newline at end of file + + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val tempTarget = selectedItems.valueAt(0) + return "${rh.gs(R.string.careportal_temporarytarget)}: ${tempTarget.friendlyDescription(profileFunction.getUnits(), rh)}\n" + + dateUtil.dateAndTimeString(tempTarget.timestamp) + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt index 6d4c5362360..d5538f3a418 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt @@ -1,11 +1,11 @@ package info.nightscout.androidaps.activities.fragments -import android.content.DialogInterface -import android.graphics.Paint import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.util.Log +import android.util.SparseArray +import android.view.* +import androidx.appcompat.widget.Toolbar +import androidx.core.util.forEach import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -36,14 +36,15 @@ import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder +import info.nightscout.androidaps.events.EventTreatmentUpdateGui import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.math.abs @@ -64,18 +65,22 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { @Inject lateinit var repository: AppRepository private var _binding: TreatmentsTempbasalsFragmentBinding? = null + // This property is only valid between onCreateView and onDestroyView. + private val binding get() = _binding!! private val millsToThePast = T.days(30).msecs() - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! + private var selectedItems: SparseArray = SparseArray() + private var showInvalidated = false + private var toolbar: Toolbar? = null + private var removeActionMode: ActionMode? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + toolbar = activity?.findViewById(R.id.toolbar) + setHasOptionsMenu(true) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) } @@ -98,7 +103,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { val now = System.currentTimeMillis() disposable += if (activePlugin.activePump.isFakingTempsByExtendedBoluses) { - if (binding.showInvalidated.isChecked) + if (showInvalidated) tempBasalsWithInvalid(now) .zipWith(extendedBolusesWithInvalid(now)) { first, second -> first + second } .map { list -> list.filterNotNull() } @@ -113,7 +118,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { .observeOn(aapsSchedulers.main) .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } } else { - if (binding.showInvalidated.isChecked) + if (showInvalidated) tempBasalsWithInvalid(now) .observeOn(aapsSchedulers.main) .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } @@ -150,6 +155,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + removeActionMode?.let { it.finish() } binding.recyclerview.adapter = null // avoid leaks _binding = null } @@ -164,7 +170,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { holder.binding.ns.visibility = (tempBasal.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = tempBasal.isValid.not().toVisibility() holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility() - val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempBasal.timestamp, tempBasalList[position-1].timestamp) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempBasal.timestamp, tempBasalList[position - 1].timestamp) holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.text = dateUtil.dateString(tempBasal.timestamp) if (tempBasal.isInProgress) { @@ -187,62 +193,147 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility() holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility() if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor) - holder.binding.remove.tag = tempBasal - + holder.binding.cbRemove.visibility = (tempBasal.isValid && removeActionMode != null).toVisibility() + if (removeActionMode != null) { + holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> + if (value) { + selectedItems.put(position, tempBasal) + } else { + selectedItems.remove(position) + } + removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) + } + holder.binding.cbRemove.isChecked = selectedItems.get(position) != null + } val nextTimestamp = if (tempBasalList.size != position + 1) tempBasalList[position + 1].timestamp else 0L holder.binding.delimiter.visibility = dateUtil.isSameDay(tempBasal.timestamp, nextTimestamp).toVisibility() } - override fun getItemCount(): Int = tempBasalList.size + override fun getItemCount() = tempBasalList.size inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val binding = TreatmentsTempbasalsItemBinding.bind(itemView) - init { - binding.remove.setOnClickListener { v: View -> - val tempBasal = v.tag as TemporaryBasal - var extendedBolus: ExtendedBolus? = null - val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED - if (isFakeExtended) { - val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet() - extendedBolus = if (eb is ValueWrapper.Existing) eb.value else null - } - val profile = profileFunction.getProfile(dateUtil.now()) - ?: return@setOnClickListener - context?.let { - OKDialog.showConfirmation(it, rh.gs(R.string.removerecord), - """ - ${if (isFakeExtended) rh.gs(R.string.extended_bolus) else rh.gs(R.string.tempbasal_label)}: ${tempBasal.toStringFull(profile, dateUtil)} - ${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.timestamp)} - """.trimIndent(), - { _: DialogInterface?, _: Int -> - if (isFakeExtended && extendedBolus != null) { - uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments, - ValueWithUnit.Timestamp(extendedBolus.timestamp), - ValueWithUnit.Insulin(extendedBolus.amount), - ValueWithUnit.UnitPerHour(extendedBolus.rate), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt())) - disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id)) - .subscribe( - { aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) }) - } else if (!isFakeExtended) { - uel.log(Action.TEMP_BASAL_REMOVED, Sources.Treatments, - ValueWithUnit.Timestamp(tempBasal.timestamp), - if (tempBasal.isAbsolute) ValueWithUnit.UnitPerHour(tempBasal.rate) else ValueWithUnit.Percent(tempBasal.rate.toInt()), - ValueWithUnit.Minute(T.msecs(tempBasal.duration).mins().toInt())) - disposable += repository.runTransactionForResult(InvalidateTemporaryBasalTransaction(tempBasal.id)) - .subscribe( - { aapsLogger.debug(LTag.DATABASE, "Removed temporary basal $tempBasal") }, - { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) }) - } - }, null) - } + } + + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_treatments_temp_basal, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated + menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated + + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { + R.id.nav_remove_items -> { + removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback()) + true + } + + R.id.nav_show_invalidated -> { + showInvalidated = true + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_hide_invalidated -> { + showInvalidated = false + rxBus.send(EventTreatmentUpdateGui()) + true + } + + else -> false + } + + inner class RemoveActionModeCallback : ActionMode.Callback { + + override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean { + mode.menuInflater.inflate(R.menu.menu_delete_selection, menu) + selectedItems.clear() + mode.title = rh.gs(R.string.count_selected, selectedItems.size()) + binding.recyclerview.adapter?.notifyDataSetChanged() + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return when (item.itemId) { + R.id.remove_selected -> { + removeSelected() + true } - binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + + else -> false } } + override fun onDestroyActionMode(mode: ActionMode?) { + removeActionMode = null + binding.recyclerview.adapter?.notifyDataSetChanged() + } + } + + private fun getConfirmationText(): String { + if (selectedItems.size() == 1) { + val tempBasal = selectedItems.valueAt(0) + val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED + val profile = profileFunction.getProfile(dateUtil.now()) + if (profile != null) + return "${if (isFakeExtended) rh.gs(R.string.extended_bolus) else rh.gs(R.string.tempbasal_label)}: ${tempBasal.toStringFull(profile, dateUtil)}\n" + + "${rh.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.timestamp)}" + } + return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) + } + + private fun removeSelected() { + if (selectedItems.size() > 0) + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { + selectedItems.forEach {_, tempBasal -> + var extendedBolus: ExtendedBolus? = null + val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED + if (isFakeExtended) { + val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet() + extendedBolus = if (eb is ValueWrapper.Existing) eb.value else null + } + if (isFakeExtended && extendedBolus != null) { + uel.log( + Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(extendedBolus.timestamp), + ValueWithUnit.Insulin(extendedBolus.amount), + ValueWithUnit.UnitPerHour(extendedBolus.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt()) + ) + disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id)) + .subscribe( + { aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) }) + } else if (!isFakeExtended) { + uel.log( + Action.TEMP_BASAL_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(tempBasal.timestamp), + if (tempBasal.isAbsolute) ValueWithUnit.UnitPerHour(tempBasal.rate) else ValueWithUnit.Percent(tempBasal.rate.toInt()), + ValueWithUnit.Minute(T.msecs(tempBasal.duration).mins().toInt()) + ) + disposable += repository.runTransactionForResult(InvalidateTemporaryBasalTransaction(tempBasal.id)) + .subscribe( + { aapsLogger.debug(LTag.DATABASE, "Removed temporary basal $tempBasal") }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) }) + } + } + removeActionMode?.finish() + }) + } + else + removeActionMode?.finish() } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt index 4d3620052b3..ac8c3b68e1d 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt @@ -1,9 +1,7 @@ package info.nightscout.androidaps.activities.fragments import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment @@ -29,7 +27,8 @@ import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -51,11 +50,10 @@ class TreatmentsUserEntryFragment : DaggerFragment() { private val millsToThePastFiltered = T.days(30).msecs() private val millsToThePastUnFiltered = T.days(3).msecs() - + private var showLoop = false private var _binding: TreatmentsUserEntryFragmentBinding? = null - // This property is only valid between onCreateView and - // onDestroyView. + // This property is only valid between onCreateView and onDestroyView. private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = @@ -63,35 +61,33 @@ class TreatmentsUserEntryFragment : DaggerFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.ueExportToXml.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") { - uel.log(Action.EXPORT_CSV, Sources.Treatments) - importExportPrefs.exportUserEntriesCsv(activity) - } + } + + fun exportUserEnteries() { + activity?.let { activity -> + OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") { + uel.log(Action.EXPORT_CSV, Sources.Treatments) + importExportPrefs.exportUserEntriesCsv(activity) } } - binding.showLoop.setOnCheckedChangeListener { _, _ -> - rxBus.send(EventTreatmentUpdateGui()) - } } fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showLoop.isChecked) - disposable.add( repository - .getUserEntryDataFromTime(now - millsToThePastUnFiltered) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } - ) - else - disposable.add( repository - .getUserEntryFilteredDataFromTime(now - millsToThePastFiltered) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } - ) + disposable += + if (showLoop) + repository + .getUserEntryDataFromTime(now - millsToThePastUnFiltered) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } + else + repository + .getUserEntryFilteredDataFromTime(now - millsToThePastFiltered) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } } @Synchronized @@ -99,15 +95,15 @@ class TreatmentsUserEntryFragment : DaggerFragment() { super.onResume() swapAdapter() - disposable.add(rxBus + disposable += rxBus .toObservable(EventPreferenceChange::class.java) .observeOn(aapsSchedulers.io) - .subscribe({ swapAdapter() }, fabricPrivacy::logException)) - disposable.add(rxBus + .subscribe({ swapAdapter() }, fabricPrivacy::logException) + disposable += rxBus .toObservable(EventTreatmentUpdateGui::class.java) .observeOn(aapsSchedulers.io) .debounce(1L, TimeUnit.SECONDS) - .subscribe({ swapAdapter() }, fabricPrivacy::logException)) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) } @Synchronized @@ -132,7 +128,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() { override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) { val current = entries[position] - val sameDayPrevious = position > 0 && dateUtil.isSameDay(current.timestamp, entries[position-1].timestamp) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(current.timestamp, entries[position - 1].timestamp) holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.text = dateUtil.dateString(current.timestamp) holder.binding.time.text = dateUtil.timeStringWithSeconds(current.timestamp) @@ -152,7 +148,40 @@ class TreatmentsUserEntryFragment : DaggerFragment() { val binding = TreatmentsUserEntryItemBinding.bind(itemView) } - override fun getItemCount(): Int = entries.size + override fun getItemCount() = entries.size + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_treatments_user_entry, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + menu.findItem(R.id.nav_hide_loop)?.isVisible = showLoop + menu.findItem(R.id.nav_show_loop)?.isVisible = !showLoop + + return super.onPrepareOptionsMenu(menu) } -} \ No newline at end of file + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { + R.id.nav_show_loop -> { + showLoop = true + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_hide_loop -> { + showLoop = false + rxBus.send(EventTreatmentUpdateGui()) + true + } + + R.id.nav_export -> { + exportUserEnteries() + true + } + + else -> false + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt index d0ec64cbdc0..fb1136bc3eb 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt @@ -7,7 +7,7 @@ import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData -import io.reactivex.disposables.Disposable +import io.reactivex.rxjava3.disposables.Disposable import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt index e6635ac1fba..f073e122f08 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -28,8 +28,8 @@ import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DecimalFormat import java.util.* import java.util.concurrent.TimeUnit diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt index 94b9cf1f388..03cb215a7fc 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt @@ -31,8 +31,8 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.extensions.fromConstant import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DecimalFormat import java.util.* import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt index 7f94aab4939..54b222ce2c8 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt @@ -29,8 +29,8 @@ import info.nightscout.shared.SafeParse import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* import javax.inject.Inject import kotlin.math.abs diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt index b785861b5b2..21eb81ebce6 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -30,8 +30,8 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.toSignedString import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.shared.SafeParse -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DecimalFormat import java.util.* import java.util.concurrent.TimeUnit diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt index 0420afc6c34..8e67a0ed6e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt @@ -40,8 +40,8 @@ import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject class LoopDialog : DaggerDialogFragment() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt index 0bed01e8523..70ed31b8bc5 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt @@ -32,8 +32,8 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DecimalFormat import java.util.* import java.util.concurrent.TimeUnit @@ -143,7 +143,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { binding.reusebutton.text = rh.gs(R.string.reuse_profile_pct_hours, profile.value.originalPercentage, T.msecs(profile.value.originalTimeshift).hours().toInt()) binding.reusebutton.setOnClickListener { binding.percentage.value = profile.value.originalPercentage.toDouble() - binding.timeshift.value = profile.value.originalTimeshift.toDouble() + binding.timeshift.value = T.msecs(profile.value.originalTimeshift).hours().toDouble() } } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt index 7e449950e2b..c1ed8faf5ba 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt @@ -28,8 +28,8 @@ import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DecimalFormat import java.util.* import java.util.concurrent.TimeUnit diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt index 46b97a3f0a2..0e8a5095172 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt @@ -31,8 +31,8 @@ import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DecimalFormat import java.util.* import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt index 83aff0201f4..fbfc23c17b3 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt @@ -33,7 +33,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP import info.nightscout.androidaps.utils.wizard.BolusWizard -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import java.text.DecimalFormat import java.util.* import javax.inject.Inject @@ -125,7 +125,9 @@ class WizardDialog : DaggerDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { loadCheckedStates() processCobCheckBox() - binding.sbCheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility() + val useSuperBolus = sp.getBoolean(R.string.key_usesuperbolus, false) + binding.sbCheckbox.visibility = useSuperBolus.toVisibility() + binding.superBolusRow.visibility = useSuperBolus.toVisibility() binding.notesLayout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value() @@ -146,7 +148,7 @@ class WizardDialog : DaggerDialogFragment() { if (correctionPercent) { calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble() - binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) + binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) binding.correctionInput.value = calculatedPercentage binding.correctionUnit.text = "%" } else { @@ -175,9 +177,10 @@ class WizardDialog : DaggerDialogFragment() { dismiss() } binding.bgCheckboxIcon.setOnClickListener { binding.bgCheckbox.isChecked = !binding.bgCheckbox.isChecked } + binding.ttCheckboxIcon.setOnClickListener { binding.ttCheckbox.isChecked = !binding.ttCheckbox.isChecked } binding.trendCheckboxIcon.setOnClickListener { binding.bgTrendCheckbox.isChecked = !binding.bgTrendCheckbox.isChecked } binding.cobCheckboxIcon.setOnClickListener { binding.cobCheckbox.isChecked = !binding.cobCheckbox.isChecked; processCobCheckBox(); } - binding.iobCheckboxIcon.setOnClickListener { if (!binding.cobCheckbox.isChecked) binding.iobCheckbox.isChecked = !binding.iobCheckbox.isChecked } + binding.iobCheckboxIcon.setOnClickListener { binding.iobCheckbox.isChecked = !binding.iobCheckbox.isChecked; processIobCheckBox(); } // cancel button binding.okcancel.cancel.setOnClickListener { aapsLogger.debug(LTag.APS, "Dialog canceled: ${this.javaClass.name}") @@ -212,7 +215,7 @@ class WizardDialog : DaggerDialogFragment() { binding.correctionUnit.text = if (isChecked) "%" else rh.gs(R.string.insulin_unit_shortname) correctionPercent = binding.correctionPercent.isChecked if (correctionPercent) { - binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) + binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) binding.correctionInput.customContentDescription = rh.gs(R.string.a11_correction_percentage) } else { binding.correctionInput.setParams( @@ -265,35 +268,42 @@ class WizardDialog : DaggerDialogFragment() { private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) { saveCheckedStates() binding.ttCheckbox.isEnabled = binding.bgCheckbox.isChecked && repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing + binding.ttCheckboxIcon.visibility = binding.ttCheckbox.isEnabled.toVisibility() if (buttonView.id == binding.cobCheckbox.id) processCobCheckBox() + if (buttonView.id == binding.iobCheckbox.id) + processIobCheckBox() processEnabledIcons() calculateInsulin() } private fun processCobCheckBox() { if (binding.cobCheckbox.isChecked) { - binding.iobCheckbox.isEnabled = false - binding.iobCheckboxIcon.isEnabled = false binding.iobCheckbox.isChecked = true - } else { - binding.iobCheckbox.isEnabled = true - binding.iobCheckboxIcon.isEnabled = true + } + } + + private fun processIobCheckBox() { + if (!binding.iobCheckbox.isChecked) { + binding.cobCheckbox.isChecked = false } } private fun processEnabledIcons() { binding.bgCheckboxIcon.isChecked = binding.bgCheckbox.isChecked + binding.ttCheckboxIcon.isChecked = binding.ttCheckbox.isChecked binding.trendCheckboxIcon.isChecked = binding.bgTrendCheckbox.isChecked binding.iobCheckboxIcon.isChecked = binding.iobCheckbox.isChecked binding.cobCheckboxIcon.isChecked = binding.cobCheckbox.isChecked binding.bgCheckboxIcon.alpha = if (binding.bgCheckbox.isChecked) 1.0f else 0.2f + binding.ttCheckboxIcon.alpha = if (binding.ttCheckbox.isChecked) 1.0f else 0.2f binding.trendCheckboxIcon.alpha = if (binding.bgTrendCheckbox.isChecked) 1.0f else 0.2f binding.iobCheckboxIcon.alpha = if (binding.iobCheckbox.isChecked) 1.0f else 0.2f binding.cobCheckboxIcon.alpha = if (binding.cobCheckbox.isChecked) 1.0f else 0.2f binding.bgCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() + binding.ttCheckboxIcon.visibility = (binding.calculationCheckbox.isChecked.not() && binding.ttCheckbox.isEnabled).toVisibility() binding.trendCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() binding.iobCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() binding.cobCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() @@ -345,7 +355,9 @@ class WizardDialog : DaggerDialogFragment() { // Set BG if not old binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0 + binding.ttCheckbox.isEnabled = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing + binding.ttCheckboxIcon.visibility = binding.ttCheckbox.isEnabled.toVisibility() // IOB calculation val bolusIob = iobCobCalculator.calculateIobFromBolus().round() 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 c7cfe5b9697..1a1ec6fae53 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 @@ -19,8 +19,8 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject class LoopFragment : DaggerFragment() { 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 09e48965255..e43c353d877 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 @@ -59,8 +59,8 @@ import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton import kotlin.math.abs 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 d9d8dd2c0bb..8ec075f1a9a 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 @@ -18,8 +18,8 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.JSONFormatter import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import org.json.JSONArray import org.json.JSONException import javax.inject.Inject 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 785066e6959..8289e60f7cc 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 @@ -20,8 +20,8 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import org.json.JSONArray import org.json.JSONException import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt index 4a84093b6a8..317a7159b07 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt @@ -258,9 +258,9 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri this.mealData.put("lastBolusTime", mealData.lastBolusTime) this.mealData.put("lastCarbTime", mealData.lastCarbTime) - this.mealData.put("TDDAIMI7", tddCalculator.averageTDD(tddCalculator.calculate(7)).totalAmount) + this.mealData.put("TDDAIMI7", tddCalculator.averageTDD(tddCalculator.calculate(7))?.totalAmount) this.mealData.put("TDDPUMP", tddCalculator.calculateDaily().totalAmount) - this.mealData.put("TDDLast24", tddCalculator!!.calculate24Daily().totalAmount) + this.mealData.put("TDDLast24", tddCalculator.calculate24Daily().totalAmount) if (constraintChecker.isAutosensModeEnabled().value()) { autosensData.put("ratio", autosensDataRatio) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/OpenAPSSMBDynamicISFPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/OpenAPSSMBDynamicISFPlugin.kt index 8c80b907387..0187b85ecee 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/OpenAPSSMBDynamicISFPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/OpenAPSSMBDynamicISFPlugin.kt @@ -65,6 +65,7 @@ class OpenAPSSMBDynamicISFPlugin @Inject constructor( pluginDescription .pluginName(R.string.openaps_smb_dynamic_isf) .description(R.string.description_smb_dynamic_isf) + .shortName(R.string.dynisf_shortname) .setDefault(false) } 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 528b89f6839..0bf7dcfc30f 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 @@ -19,13 +19,13 @@ import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui import info.nightscout.androidaps.utils.FabricPrivacy -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.kotlin.plusAssign import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import java.util.* import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt index 405daabc9ec..63e03478dc9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt @@ -22,8 +22,8 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt index 74ab3aef5e1..800994fe035 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt @@ -12,8 +12,8 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton import kotlin.math.abs 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 f1f83e5603a..d4d0c628a95 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 @@ -37,11 +37,11 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.SntpClient import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.kotlin.plusAssign import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject class ObjectivesFragment : DaggerFragment() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt index cff84db0289..bb1dcae6ede 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt @@ -9,6 +9,7 @@ import info.nightscout.androidaps.interfaces.* import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin +import info.nightscout.androidaps.plugins.aps.openAPSSMBDynamicISF.OpenAPSSMBDynamicISFPlugin import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification @@ -36,6 +37,7 @@ class SafetyPlugin @Inject constructor( private val constraintChecker: ConstraintChecker, private val openAPSAMAPlugin: OpenAPSAMAPlugin, private val openAPSSMBPlugin: OpenAPSSMBPlugin, + private val OpenAPSSMBDynamicISFPlugin: OpenAPSSMBDynamicISFPlugin, private val sensitivityOref1Plugin: SensitivityOref1Plugin, private val activePlugin: ActivePlugin, private val hardLimits: HardLimits, @@ -188,10 +190,12 @@ class SafetyPlugin @Inject constructor( override fun applyMaxIOBConstraints(maxIob: Constraint): Constraint { val apsMode = sp.getString(R.string.key_aps_mode, "open") - val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled()) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string.key_openapsma_max_iob, 1.5) + val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled() || OpenAPSSMBDynamicISFPlugin.isEnabled()) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string + .key_openapsma_max_iob, 1.5) maxIob.setIfSmaller(aapsLogger, maxIobPref, String.format(rh.gs(R.string.limitingiob), maxIobPref, rh.gs(R.string.maxvalueinpreferences)), this) if (openAPSAMAPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), String.format(rh.gs(R.string.limitingiob), hardLimits.maxIobAMA(), rh.gs(R.string.hardlimit)), this) if (openAPSSMBPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(rh.gs(R.string.limitingiob), hardLimits.maxIobSMB(), rh.gs(R.string.hardlimit)), this) + if (OpenAPSSMBDynamicISFPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(rh.gs(R.string.limitingiob), hardLimits.maxIobSMB(), rh.gs(R.string.hardlimit)), this) if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, String.format(rh.gs(R.string.limitingiob), HardLimits.MAX_IOB_LGS, rh.gs(R.string.lowglucosesuspend)), this) return maxIob } @@ -207,4 +211,4 @@ class SafetyPlugin @Inject constructor( configuration.storeDouble(R.string.key_treatmentssafety_maxbolus, sp, rh) configuration.storeInt(R.string.key_treatmentssafety_maxcarbs, sp, rh) } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt index 111df213694..dc5805cfd14 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt @@ -49,8 +49,8 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP import info.nightscout.androidaps.utils.ui.SingleClickButton import info.nightscout.androidaps.utils.ui.UIRunnable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt index 5b10d02649e..035c3a13a10 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt @@ -25,7 +25,7 @@ import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt index 4507d85ab37..eae7c2b177f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt @@ -36,10 +36,10 @@ import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.ui.UIRunnable -import io.reactivex.Completable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy +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 java.util.* import java.util.concurrent.TimeUnit import javax.inject.Inject 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 b39883538bd..8d6cc9c91b4 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 @@ -29,9 +29,9 @@ import info.nightscout.androidaps.plugins.pump.omnipod.eros.history.database.Ero import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.Completable.fromAction -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.subscribeBy +import io.reactivex.rxjava3.core.Completable.fromAction +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.subscribeBy import javax.inject.Inject class MaintenanceFragment : DaggerFragment() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt index 78f69cf5deb..7c01449ba67 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt @@ -310,7 +310,7 @@ class NSClientAddUpdateWorker( } ?: aapsLogger.error("Error parsing TherapyEvent json $json") } eventType == TherapyEvent.Type.COMBO_BOLUS.text -> - if (config.NSCLIENT) { + if (buildHelper.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) { extendedBolusFromJson(json)?.let { extendedBolus -> repository.runTransactionForResult(SyncNsExtendedBolusTransaction(extendedBolus)) .doOnError { @@ -356,7 +356,7 @@ class NSClientAddUpdateWorker( } ?: aapsLogger.error("Error parsing ExtendedBolus json $json") } eventType == TherapyEvent.Type.TEMPORARY_BASAL.text -> - if (config.NSCLIENT) { + if (buildHelper.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) { temporaryBasalFromJson(json)?.let { temporaryBasal -> repository.runTransactionForResult(SyncNsTemporaryBasalTransaction(temporaryBasal)) .doOnError { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt index 13b5469d019..63351dc7bae 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt @@ -21,8 +21,8 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject class NSClientFragment : DaggerFragment() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt index 540ce3e628e..d63cf23512b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt @@ -38,7 +38,7 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import java.util.* import javax.inject.Inject import javax.inject.Singleton @@ -159,6 +159,7 @@ class NSClientPlugin @Inject constructor( // preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_carbs))?.isVisible = buildHelper.isEngineeringMode() // preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_temp_target))?.isVisible = buildHelper.isEngineeringMode() } + preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_tbr_eb))?.isVisible = buildHelper.isEngineeringMode() } private val mConnection: ServiceConnection = object : ServiceConnection { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt index 040fe824535..970b047ddbc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt @@ -49,7 +49,7 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import io.socket.client.IO import io.socket.client.Socket import io.socket.emitter.Emitter 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 67f6c4e13ba..a3b4f11b150 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 @@ -78,8 +78,8 @@ import info.nightscout.shared.sharedPreferences.SP import info.nightscout.androidaps.utils.ui.SingleClickButton import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.wizard.QuickWizard -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* import java.util.concurrent.TimeUnit import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt index f9c34a49b14..8685abdbe31 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt @@ -24,8 +24,8 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import org.json.JSONObject import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt index 065a2f1fc32..c3e3fd1b9f1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.os.Bundle import android.util.Log import android.view.LayoutInflater +import android.view.MenuItem import android.view.MotionEvent import android.view.View import android.view.ViewGroup @@ -12,26 +13,30 @@ import android.widget.ImageView import android.widget.TextView import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.ItemTouchHelper.* +import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG +import androidx.recyclerview.widget.ItemTouchHelper.DOWN +import androidx.recyclerview.widget.ItemTouchHelper.END +import androidx.recyclerview.widget.ItemTouchHelper.START +import androidx.recyclerview.widget.ItemTouchHelper.UP import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import info.nightscout.androidaps.R -import info.nightscout.androidaps.activities.NoSplashAppCompatActivity +import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult import info.nightscout.androidaps.databinding.OverviewQuickwizardlistActivityBinding import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy -import io.reactivex.rxkotlin.plusAssign import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.androidaps.utils.wizard.QuickWizardEntry import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject -class QuickWizardListActivity : NoSplashAppCompatActivity() { +class QuickWizardListActivity : DaggerAppCompatActivityWithResult() { @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBus @@ -176,6 +181,10 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() { binding = OverviewQuickwizardlistActivityBinding.inflate(layoutInflater) setContentView(binding.root) + title = rh.gs(R.string.quickwizard) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(this) binding.recyclerview.adapter = RecyclerViewAdapter(supportFragmentManager) @@ -203,4 +212,14 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() { disposable.clear() super.onPause() } + + override fun onOptionsItemSelected(item: MenuItem): Boolean = + when (item.itemId) { + android.R.id.home -> { + finish() + true + } + + else -> false + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt index e7abb4690d7..af5593e3ca9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt @@ -105,7 +105,8 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { binding.from.setOnClickListener { context?.let { TimePickerDialog( - it, fromTimeSetListener, + it, R.style.MaterialPickerTheme, + fromTimeSetListener, T.secs(fromSeconds.toLong()).hours().toInt(), T.secs((fromSeconds % 3600).toLong()).mins().toInt(), DateFormat.is24HourFormat(context) @@ -123,7 +124,8 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { binding.to.setOnClickListener { context?.let { TimePickerDialog( - it, toTimeSetListener, + it, R.style.MaterialPickerTheme, + toTimeSetListener, T.secs(toSeconds.toLong()).hours().toInt(), T.secs((toSeconds % 3600).toLong()).mins().toInt(), DateFormat.is24HourFormat(context) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt index fe0d90116a5..e6651412aea 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt @@ -13,7 +13,7 @@ import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject /** diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt index 555dba32393..3bd76dab563 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt @@ -20,8 +20,8 @@ import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt index 6f422dad47d..666097d0173 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorFragment.kt @@ -11,9 +11,9 @@ import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSm import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.kotlin.plusAssign import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import java.util.* import javax.inject.Inject import kotlin.math.max diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 4f733dded36..d2c12037ce6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -48,8 +48,8 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP import info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference import info.nightscout.shared.SafeParse -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import org.apache.commons.lang3.StringUtils import org.joda.time.DateTime import java.text.Normalizer @@ -67,7 +67,7 @@ class SmsCommunicatorPlugin @Inject constructor( injector: HasAndroidInjector, aapsLogger: AAPSLogger, rh: ResourceHelper, - private val smsManager: SmsManager, + private val smsManager: SmsManager?, private val aapsSchedulers: AapsSchedulers, private val sp: SP, private val constraintChecker: ConstraintChecker, @@ -165,7 +165,7 @@ class SmsCommunicatorPlugin @Inject constructor( override fun updatePreferenceSummary(pref: Preference) { super.updatePreferenceSummary(pref) if (pref is EditTextPreference) { - if (pref.getKey().contains("smscommunicator_allowednumbers") && (pref.text == null || TextUtils.isEmpty(pref.text.trim { it <= ' ' }))) { + if (pref.getKey().contains("smscommunicator_allowednumbers") && (TextUtils.isEmpty(pref.text?.trim { it <= ' ' }))) { pref.setSummary(rh.gs(R.string.smscommunicator_allowednumbers_summary)) } } @@ -1100,10 +1100,10 @@ class SmsCommunicatorPlugin @Inject constructor( sms.text = stripAccents(sms.text) try { aapsLogger.debug(LTag.SMS, "Sending SMS to " + sms.phoneNumber + ": " + sms.text) - if (sms.text.toByteArray().size <= 140) smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null) + if (sms.text.toByteArray().size <= 140) smsManager?.sendTextMessage(sms.phoneNumber, null, sms.text, null, null) else { - val parts = smsManager.divideMessage(sms.text) - smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts, + val parts = smsManager?.divideMessage(sms.text) + smsManager?.sendMultipartTextMessage(sms.phoneNumber, null, parts, null, null) } messages.add(sms) 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 1daf65abc47..db3ba0ba7ff 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 @@ -14,10 +14,10 @@ import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolD import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolResetData import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolUpdateGUI import info.nightscout.androidaps.utils.FabricPrivacy -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.kotlin.plusAssign import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject class TidepoolFragment : DaggerFragment() { 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 045f638e6a0..05d0dfbc232 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 @@ -30,11 +30,11 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.kotlin.plusAssign import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import java.util.* import javax.inject.Inject import javax.inject.Singleton @@ -68,6 +68,7 @@ class TidepoolPlugin @Inject constructor( private val listLog = ArrayList() var textLog: Spanned = HtmlHelper.fromHtml("") + @Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "UNNECESSARY_NOT_NULL_ASSERTION") override fun onStart() { super.onStart() disposable += rxBus diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt index 124f00e4ac9..481e904868d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt @@ -42,8 +42,8 @@ import info.nightscout.shared.sharedPreferences.SP import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.shared.SafeParse -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt index b43775cdaa7..d6be8f54cd4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt @@ -20,7 +20,7 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatusLinePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatusLinePlugin.kt index aa41940fd69..825ae8ad5ac 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatusLinePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatusLinePlugin.kt @@ -15,8 +15,8 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt index 6f623266ca2..d7991a67075 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt @@ -34,8 +34,8 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import org.json.JSONArray import java.util.concurrent.Executors import java.util.concurrent.ScheduledFuture diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt index 1fa1f3711f1..3a81af67d3f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt @@ -24,16 +24,18 @@ import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged -import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.androidaps.utils.ui.SpinnerHelper import info.nightscout.androidaps.utils.ui.TimeListEdit import info.nightscout.shared.SafeParse import info.nightscout.shared.logging.AAPSLogger -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.math.RoundingMode import java.text.DecimalFormat import javax.inject.Inject @@ -54,12 +56,12 @@ class LocalProfileFragment : DaggerFragment() { private var disposable: CompositeDisposable = CompositeDisposable() private var basalView: TimeListEdit? = null - private var spinner: SpinnerHelper? = null +// private var spinner: SpinnerHelper? = null private val save = Runnable { doEdit() basalView?.updateLabel(rh.gs(R.string.basal_label) + ": " + sumLabel()) - localProfilePlugin.profile?.getSpecificProfile(spinner?.selectedItem.toString())?.let { + localProfilePlugin.getEditedProfile()?.let { binding.basalGraph.show(ProfileSealed.Pure(it)) binding.icGraph.show(ProfileSealed.Pure(it)) binding.isfGraph.show(ProfileSealed.Pure(it)) @@ -79,7 +81,7 @@ class LocalProfileFragment : DaggerFragment() { } private fun sumLabel(): String { - val profile = localProfilePlugin.getEditProfile() + val profile = localProfilePlugin.getEditedProfile() val sum = profile?.let { ProfileSealed.Pure(profile).baseBasalSum() } ?: 0.0 return " ∑" + DecimalFormatter.to2Decimal(sum) + rh.gs(R.string.insulin_unit_shortname) } @@ -135,56 +137,115 @@ class LocalProfileFragment : DaggerFragment() { binding.name.addTextChangedListener(textWatch) binding.dia.setParams(currentProfile.dia, hardLimits.minDia(), hardLimits.maxDia(), 0.1, DecimalFormat("0.0"), false, null, textWatch) binding.dia.tag = "LP_DIA" - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.ic_holder, "IC", rh.gs(R.string.ic_long_label), currentProfile.ic, null, doubleArrayOf(hardLimits.minIC(), hardLimits.maxIC()), null, 0.1, DecimalFormat ("0.0"), save) - basalView = TimeListEdit(context, aapsLogger, dateUtil, view, R.id.basal_holder, "BASAL", rh.gs(R.string.basal_long_label) + ": " + sumLabel(), currentProfile.basal, null, doubleArrayOf(pumpDescription.basalMinimumRate, pumpDescription.basalMaximumRate), null, 0.01, DecimalFormat("0.00"), save) + TimeListEdit( + context, + aapsLogger, + dateUtil, + view, + R.id.ic_holder, + "IC", + rh.gs(R.string.ic_long_label), + currentProfile.ic, + null, + doubleArrayOf(hardLimits.minIC(), hardLimits.maxIC()), + null, + 0.1, + DecimalFormat("0.0"), + save + ) + basalView = + TimeListEdit( + context, + aapsLogger, + dateUtil, + view, + R.id.basal_holder, + "BASAL", + rh.gs(R.string.basal_long_label) + ": " + sumLabel(), + currentProfile.basal, + null, + doubleArrayOf(pumpDescription.basalMinimumRate, pumpDescription.basalMaximumRate), + null, + 0.01, + DecimalFormat("0.00"), + save + ) if (units == Constants.MGDL) { val isfRange = doubleArrayOf(HardLimits.MIN_ISF, HardLimits.MAX_ISF) - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf_holder, "ISF", rh.gs(R.string.isf_long_label), currentProfile.isf, null, isfRange , null, 1.0, DecimalFormat("0"), save) - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target_holder, "TARGET", rh.gs(R.string.target_long_label), currentProfile.targetLow, currentProfile.targetHigh, HardLimits.VERY_HARD_LIMIT_MIN_BG, HardLimits.VERY_HARD_LIMIT_TARGET_BG, 1.0, DecimalFormat("0"), save) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf_holder, "ISF", rh.gs(R.string.isf_long_label), currentProfile.isf, null, isfRange, null, 1.0, DecimalFormat("0"), save) + TimeListEdit( + context, + aapsLogger, + dateUtil, + view, + R.id.target_holder, + "TARGET", + rh.gs(R.string.target_long_label), + currentProfile.targetLow, + currentProfile.targetHigh, + HardLimits.VERY_HARD_LIMIT_MIN_BG, + HardLimits.VERY_HARD_LIMIT_TARGET_BG, + 1.0, + DecimalFormat("0"), + save + ) } else { - val isfRange = doubleArrayOf(roundUp(Profile.fromMgdlToUnits(HardLimits.MIN_ISF, GlucoseUnit.MMOL)), - roundDown(Profile.fromMgdlToUnits(HardLimits.MAX_ISF, GlucoseUnit.MMOL))) - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf_holder, "ISF", rh.gs(R.string.isf_long_label), currentProfile.isf, null,isfRange , null, 0.1, DecimalFormat("0.0"), save) - val range1 = doubleArrayOf(roundUp(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MIN_BG[0], GlucoseUnit.MMOL)), - roundDown(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MIN_BG[1], GlucoseUnit.MMOL))) - val range2 = doubleArrayOf(roundUp(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MAX_BG[0], GlucoseUnit.MMOL)), - roundDown(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MAX_BG[1], GlucoseUnit.MMOL))) - Log.i("TimeListEdit", "build: range1" + range1[0] + " " + range1[1] + " range2" + range2[0] + " " + range2[1]) - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target_holder, "TARGET", rh.gs(R.string.target_long_label), currentProfile.targetLow, currentProfile.targetHigh, range1 , range2, 0.1, DecimalFormat("0.0"), save) + val isfRange = doubleArrayOf( + roundUp(Profile.fromMgdlToUnits(HardLimits.MIN_ISF, GlucoseUnit.MMOL)), + roundDown(Profile.fromMgdlToUnits(HardLimits.MAX_ISF, GlucoseUnit.MMOL)) + ) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf_holder, "ISF", rh.gs(R.string.isf_long_label), currentProfile.isf, null, isfRange, null, 0.1, DecimalFormat("0.0"), save) + val range1 = doubleArrayOf( + roundUp(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MIN_BG[0], GlucoseUnit.MMOL)), + roundDown(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MIN_BG[1], GlucoseUnit.MMOL)) + ) + val range2 = doubleArrayOf( + roundUp(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MAX_BG[0], GlucoseUnit.MMOL)), + roundDown(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MAX_BG[1], GlucoseUnit.MMOL)) + ) + Log.i("TimeListEdit", "build: range1" + range1[0] + " " + range1[1] + " range2" + range2[0] + " " + range2[1]) + TimeListEdit( + context, + aapsLogger, + dateUtil, + view, + R.id.target_holder, + "TARGET", + rh.gs(R.string.target_long_label), + currentProfile.targetLow, + currentProfile.targetHigh, + range1, + range2, + 0.1, + DecimalFormat("0.0"), + save + ) } // Spinner - spinner = SpinnerHelper(binding.spinner) context?.let { context -> val profileList: ArrayList = localProfilePlugin.profile?.getProfileList() ?: ArrayList() - spinner?.adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) - val selection = localProfilePlugin.currentProfileIndex - if (selection in 0 until profileList.size) spinner?.setSelection(selection) + binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList)) } ?: return - spinner?.setOnItemSelectedListener(object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) { - } - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - if (localProfilePlugin.isEdited) { - activity?.let { activity -> - OKDialog.showConfirmation(activity, rh.gs(R.string.doyouwantswitchprofile), { + binding.profileList.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> + if (localProfilePlugin.isEdited) { + activity?.let { activity -> + OKDialog.showConfirmation( + activity, rh.gs(R.string.doyouwantswitchprofile), + { localProfilePlugin.currentProfileIndex = position localProfilePlugin.isEdited = false build() - }, { - val selection = localProfilePlugin.currentProfileIndex - if (selection in 0 until (spinner?.adapter?.count ?: -1)) spinner?.setSelection(selection) - } - ) - } - } else { - localProfilePlugin.currentProfileIndex = position - build() + }, null + ) } + } else { + localProfilePlugin.currentProfileIndex = position + build() } - }) - localProfilePlugin.profile?.getSpecificProfile(spinner?.selectedItem.toString())?.let { + } + localProfilePlugin.getEditedProfile()?.let { binding.basalGraph.show(ProfileSealed.Pure(it)) binding.icGraph.show(ProfileSealed.Pure(it)) binding.isfGraph.show(ProfileSealed.Pure(it)) @@ -206,8 +267,12 @@ class LocalProfileFragment : DaggerFragment() { if (localProfilePlugin.isEdited) { activity?.let { OKDialog.show(it, "", rh.gs(R.string.saveorresetchangesfirst)) } } else { - uel.log(Action.CLONE_PROFILE, Sources.LocalProfile, ValueWithUnit.SimpleString(localProfilePlugin.currentProfile()?.name - ?: "")) + uel.log( + Action.CLONE_PROFILE, Sources.LocalProfile, ValueWithUnit.SimpleString( + localProfilePlugin.currentProfile()?.name + ?: "" + ) + ) localProfilePlugin.cloneProfile() build() } @@ -216,8 +281,12 @@ class LocalProfileFragment : DaggerFragment() { binding.profileRemove.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.deletecurrentprofile), { - uel.log(Action.PROFILE_REMOVED, Sources.LocalProfile, ValueWithUnit.SimpleString(localProfilePlugin.currentProfile()?.name - ?: "")) + uel.log( + Action.PROFILE_REMOVED, Sources.LocalProfile, ValueWithUnit.SimpleString( + localProfilePlugin.currentProfile()?.name + ?: "" + ) + ) localProfilePlugin.removeCurrentProfile() build() }, null) @@ -245,8 +314,12 @@ class LocalProfileFragment : DaggerFragment() { if (!localProfilePlugin.isValidEditState(activity)) { return@setOnClickListener //Should not happen as saveButton should not be visible if not valid } - uel.log(Action.STORE_PROFILE, Sources.LocalProfile, ValueWithUnit.SimpleString(localProfilePlugin.currentProfile()?.name - ?: "")) + uel.log( + Action.STORE_PROFILE, Sources.LocalProfile, ValueWithUnit.SimpleString( + localProfilePlugin.currentProfile()?.name + ?: "" + ) + ) localProfilePlugin.storeSettings(activity) build() } @@ -294,7 +367,7 @@ class LocalProfileFragment : DaggerFragment() { val isEdited = localProfilePlugin.isEdited if (isValid) { this.view?.setBackgroundColor(rh.gc(R.color.ok_background)) - binding.spinner.isEnabled = true + binding.profileList.isEnabled = true if (isEdited) { //edited profile -> save first @@ -306,7 +379,7 @@ class LocalProfileFragment : DaggerFragment() { } } else { this.view?.setBackgroundColor(rh.gc(R.color.error_background)) - binding.spinner.isEnabled = false + binding.profileList.isEnabled = false binding.profileswitch.visibility = View.GONE binding.save.visibility = View.GONE //don't save an invalid profile } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt index cce7fc55d05..f131a221227 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt @@ -170,7 +170,7 @@ class LocalProfilePlugin @Inject constructor( } @Synchronized - fun getEditProfile(): PureProfile? { + fun getEditedProfile(): PureProfile? { val profile = JSONObject() with(profiles[currentProfileIndex]) { profile.put("dia", dia) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt index 382efc02ccb..1eb807dbfc0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt @@ -21,8 +21,8 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject class VirtualPumpFragment : DaggerFragment() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt index d4a0ba1687f..f900d942daf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt @@ -28,8 +28,8 @@ import info.nightscout.androidaps.utils.TimeChangeType import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import org.json.JSONException import org.json.JSONObject import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt index 901bed4b26e..d285db5f0a6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt @@ -34,8 +34,8 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.concurrent.TimeUnit import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/GlunovoPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/GlunovoPlugin.kt index c65691dfbef..818951fff1d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/GlunovoPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/GlunovoPlugin.kt @@ -26,7 +26,7 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt index 82d353a0950..9f149f7a7b1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt @@ -18,8 +18,8 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt index 5315d157136..1bf2891a816 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt @@ -41,9 +41,9 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import io.reactivex.rxjava3.kotlin.subscribeBy import java.util.* import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.kt b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.kt index 5f99519df17..3bf816ade28 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.kt @@ -52,7 +52,7 @@ class QueueThread internal constructor( val secondsElapsed = (System.currentTimeMillis() - connectionStartTime) / 1000 val pump = activePlugin.activePump // Manifest.permission.BLUETOOTH_CONNECT - if (config.PUMPDRIVERS && Build.VERSION.SDK_INT >= /*Build.VERSION_CODES.S*/31) + if (config.PUMPDRIVERS && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) if (androidPermission.permissionNotGranted(context, "android.permission.BLUETOOTH_CONNECT")) { aapsLogger.debug(LTag.PUMPQUEUE, "no permission") rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTING)) diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.kt index 2b4d667758e..c1cfb270aa9 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.kt @@ -8,7 +8,7 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.events.EventStatus import info.nightscout.androidaps.setupwizard.elements.SWItem import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject class SWEventListener constructor( diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt index eef7197a7b2..090b9ae07de 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt @@ -24,7 +24,7 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.locale.LocaleHelper.update import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.CompositeDisposable import javax.inject.Inject import kotlin.math.max import kotlin.math.min diff --git a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt index e69156470b4..3ccd6d69003 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.kt @@ -97,7 +97,7 @@ class AndroidPermission @Inject constructor( @Synchronized fun notifyForBtConnectPermission(activity: FragmentActivity) { - if (Build.VERSION.SDK_INT >= /*Build.VERSION_CODES.S*/31) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Manifest.permission.BLUETOOTH_CONNECT if (permissionNotGranted(activity, "android.permission.BLUETOOTH_CONNECT") || permissionNotGranted(activity, "android.permission.BLUETOOTH_SCAN")) { val notification = NotificationWithAction(injector, Notification.PERMISSION_BT, rh.gs(R.string.needconnectpermission), Notification.URGENT) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.kt b/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.kt index 2f1ea9e990b..063fd3137e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.kt @@ -22,8 +22,8 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton import kotlin.math.min diff --git a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt index b51e5960e62..46756397d5e 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt @@ -28,7 +28,7 @@ object PrefImportSummaryDialog { @SuppressLint("InflateParams") fun showSummary(context: Context, importOk: Boolean, importPossible: Boolean, prefs: Prefs, ok: (() -> Unit)?, cancel: (() -> Unit)? = null) { - @StyleRes val theme: Int = if (importOk) R.style.AppTheme else { + @StyleRes val theme: Int = if (importOk) R.style.DialogTheme else { if (importPossible) R.style.AppThemeWarningDialog else R.style.AppThemeErrorDialog } @@ -92,7 +92,7 @@ object PrefImportSummaryDialog { webView.setBackgroundColor(Color.TRANSPARENT) webView.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null) - AlertDialogHelper.Builder(context, R.style.AppTheme) + AlertDialogHelper.Builder(context, R.style.DialogTheme) .setCustomTitle( AlertDialogHelper.buildCustomTitle( context, diff --git a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt index a2c4e78e2ff..0bfd1f72f9b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt @@ -19,7 +19,7 @@ object TwoMessagesAlertDialog { val secondMessageLayout = LayoutInflater.from(context).inflate(R.layout.dialog_alert_two_messages, null) (secondMessageLayout.findViewById(R.id.password_prompt_title) as TextView).text = secondMessage - val dialog = AlertDialogHelper.Builder(context) + AlertDialogHelper.Builder(context, R.style.DialogTheme) .setMessage(message) .setCustomTitle( AlertDialogHelper.buildCustomTitle( @@ -40,7 +40,7 @@ object TwoMessagesAlertDialog { if (cancel != null) runOnUiThread { cancel() } } .show() - dialog.setCanceledOnTouchOutside(false) + .setCanceledOnTouchOutside(false) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt index 34663d0800b..349dd597c73 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt @@ -6,18 +6,18 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.TotalDailyDose +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.toText import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.MidnightTime import info.nightscout.androidaps.utils.T -import info.nightscout.androidaps.extensions.convertedToAbsolute -import info.nightscout.androidaps.extensions.toText import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag import javax.inject.Inject class TddCalculator @Inject constructor( @@ -76,8 +76,8 @@ class TddCalculator @Inject constructor( return result } - fun calculateDaily():TotalDailyDose { - val startTime = MidnightTime.calc(dateUtil.now() ) + fun calculateDaily(): TotalDailyDose { + val startTime = MidnightTime.calc(dateUtil.now()) val endTime = dateUtil.now() val tdd = TotalDailyDose(timestamp = startTime) //val result = TotalDailyDose() @@ -118,12 +118,12 @@ class TddCalculator @Inject constructor( tdd.totalAmount = tdd.bolusAmount + tdd.basalAmount //} - aapsLogger.debug(LTag.CORE, tdd.toString()) return tdd } - fun calculate24Daily():TotalDailyDose { - val startTime = dateUtil.now() - T.hours(hour = 24).msecs() + + fun calculate24Daily(): TotalDailyDose { + val startTime = dateUtil.now() - T.hours(hour = 24).msecs() val endTime = dateUtil.now() val tdd = TotalDailyDose(timestamp = startTime) //val result = TotalDailyDose() @@ -142,7 +142,7 @@ class TddCalculator @Inject constructor( //result.put(midnight, tdd) } val calculationStep = T.mins(5).msecs() - val tempBasals = iobCobCalculator.getTempBasalIncludingConvertedExtendedForRange(startTime, endTime, calculationStep) + //val tempBasals = iobCobCalculator.getTempBasalIncludingConvertedExtendedForRange(startTime, endTime, calculationStep) for (t in startTime until endTime step calculationStep) { //val midnight = MidnightTime.calc(t) @@ -165,12 +165,13 @@ class TddCalculator @Inject constructor( tdd.totalAmount = tdd.bolusAmount + tdd.basalAmount //} - aapsLogger.debug(LTag.CORE, tdd.toString()) return tdd } - fun averageTDD(tdds: LongSparseArray): TotalDailyDose { + + fun averageTDD(tdds: LongSparseArray): TotalDailyDose? { val totalTdd = TotalDailyDose(timestamp = dateUtil.now()) + if (tdds.size() == 0) return null for (i in 0 until tdds.size()) { val tdd = tdds.valueAt(i) totalTdd.basalAmount += tdd.basalAmount @@ -189,10 +190,11 @@ class TddCalculator @Inject constructor( val tdds = calculate(7) val averageTdd = averageTDD(tdds) return HtmlHelper.fromHtml( - "" + rh.gs(R.string.tdd) + ":
" + + if (averageTdd != null) "" + rh.gs(R.string.tdd) + ":
" + toText(tdds, true) + "" + rh.gs(R.string.average) + ":
" + averageTdd.toText(rh, tdds.size(), true) + else "" ) } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt index de30b2f5654..dca32f246af 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt @@ -33,8 +33,8 @@ import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.shared.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* import javax.inject.Inject import kotlin.math.abs diff --git a/app/src/main/res/drawable/cb_background_tt.xml b/app/src/main/res/drawable/cb_background_tt.xml new file mode 100644 index 00000000000..291b9f535ea --- /dev/null +++ b/app/src/main/res/drawable/cb_background_tt.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/drawable/checkbox_tt_icon.xml b/app/src/main/res/drawable/checkbox_tt_icon.xml new file mode 100644 index 00000000000..2c3522ae2b2 --- /dev/null +++ b/app/src/main/res/drawable/checkbox_tt_icon.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_local_activate.xml b/app/src/main/res/drawable/ic_local_activate.xml index 905ade57d3e..b6ef8ff9736 100644 --- a/app/src/main/res/drawable/ic_local_activate.xml +++ b/app/src/main/res/drawable/ic_local_activate.xml @@ -5,8 +5,8 @@ android:viewportHeight="24"> + android:fillColor="@color/ic_local_activate"/> + android:fillColor="@color/ic_local_activate"/> diff --git a/app/src/main/res/drawable/ic_local_reset.xml b/app/src/main/res/drawable/ic_local_reset.xml index 07faa6ee4db..b1008a92da1 100644 --- a/app/src/main/res/drawable/ic_local_reset.xml +++ b/app/src/main/res/drawable/ic_local_reset.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:fillColor="@color/ic_local_reset"/> diff --git a/app/src/main/res/layout/actions_fragment.xml b/app/src/main/res/layout/actions_fragment.xml index 7d43edb8578..9e2810b77bd 100644 --- a/app/src/main/res/layout/actions_fragment.xml +++ b/app/src/main/res/layout/actions_fragment.xml @@ -27,14 +27,14 @@ -