diff --git a/app/build.gradle b/app/build.gradle
index cc26a49966..df720e4e89 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"
+ version "3.0.0.1-dev-b"
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 92f076e434..a163b3729c 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" />
0){
- if ( tdd_pump > tdd7 && now < 5 || now < 7 && TDD < ( 0.8 * tdd7 ) ){
+ /*if ( tdd_pump > tdd7 && now < 5 || now < 7 && TDD < ( 0.8 * tdd7 ) ){
TDD = ( 0.8 * tdd7 );
console.log("Excess or too low insulin from pump so TDD set to "+TDD+" based on 75% of TDD7; ");
rT.reason += "TDD: " +TDD+ " due to low or high tdd from pump; ";
@@ -316,12 +334,13 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
else {
console.log("TDD 7 ="+tdd7+", TDD Pump ="+tdd_pump+" and TDD = "+TDD+";");
rT.reason += "TDD: " +TDD+ " based on standard pump 60/tdd7 40 split; ";
- }
+ }*/
var variable_sens = (277700 / (TDD * bg));
variable_sens = round(variable_sens,1);
console.log("Current sensitivity for predictions is " +variable_sens+" based on current bg");
+ console.error(" ");
sens = variable_sens;
if ( high_temptarget_raises_sensitivity && profile.temptargetSet && target_bg > normalTarget || profile.low_temptarget_lowers_sensitivity && profile.temptargetSet && target_bg < normalTarget ) {
@@ -389,7 +408,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
if (iob_data.iob > 0) {
var naive_eventualBG = round( bg - (iob_data.iob * sens) );
} else { // if IOB is negative, be more conservative and use the lower of sens, profile.sens
- naive_eventualBG = round( bg - (iob_data.iob * Math.min(sens, profile.sens) ) );
+ naive_eventualBG = round( bg - (iob_data.iob * sens ) );
}
// and adjust it for the deviation above
var eventualBG = naive_eventualBG + deviation;
diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt
index 46e6913b47..f88ac370e7 100644
--- a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt
+++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt
@@ -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/activities/HistoryBrowseActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt
index 3034cb7d7f..3cf77eaf74 100644
--- a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt
+++ b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt
@@ -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 62435f5d19..8770925cb5 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 25fc86cf2e..8de1ff39d1 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 9a68aeaf5f..5290b48711 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 da98848fcd..1c569a78a6 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
@@ -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 db151a6e2e..6e0bfcf4a7 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
@@ -57,61 +58,61 @@ class TreatmentsCareportalFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger
- private val disposable = CompositeDisposable()
-
- private val millsToThePast = T.days(30).msecs()
-
private var _binding: TreatmentsCareportalFragmentBinding? = 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 =
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)
- }
- }
+ }
- 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 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())
+ })
+ }
+ }
+
+ 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)
- }
- }
- binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
- }
}
}
-}
\ No newline at end of file
+
+ 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
+ }
+
+ 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())
+ }
+
+ 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 af8a39fe16..e1d8d4b34d 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,10 +27,10 @@ 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
@@ -39,6 +39,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
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)
- }
- }
- binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
- }
}
}
-}
\ No newline at end of file
+
+ 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
+ }
+
+ 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()
+ }
+}
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 67a2cc512a..08d4e20240 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,6 +39,8 @@ 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.rxjava3.core.Completable
import io.reactivex.rxjava3.disposables.CompositeDisposable
@@ -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 ec01cc7b3a..777a61a932 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,6 +41,7 @@ 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
@@ -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)
- }
- }
- binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
- }
}
}
-}
\ 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 { _, 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
+ }
+
+ else -> false
+ }
+ }
+
+ override fun onDestroyActionMode(mode: ActionMode?) {
+ removeActionMode = null
+ binding.recyclerview.adapter?.notifyDataSetChanged()
+ }
+ }
+
+ 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 75c0b3cc19..d5538f3a41 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,6 +36,7 @@ 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
@@ -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)
- }
- }
- binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
- }
}
}
-}
\ No newline at end of file
+
+ 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
+ }
+
+ 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()
+ }
+}
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 f0fcf32180..ac8c3b68e1 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
@@ -30,6 +28,7 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
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
}
-}
\ No newline at end of file
+ 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)
+ }
+
+ 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/dialogs/ProfileSwitchDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt
index c64539af14..70ed31b8bc 100644
--- a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt
+++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt
@@ -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/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt
index b7f943a313..fbfc23c17b 100644
--- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt
+++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt
@@ -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/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt
index 950a3e9f26..317a7159b0 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,8 +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)
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 8c80b90738..0187b85ece 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/constraints/safety/SafetyPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt
index cff84db028..bb1dcae6ed 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/overview/activities/QuickWizardListActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt
index 7718b9eaae..c3e3fd1b9f 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
@@ -20,7 +21,7 @@ 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
@@ -35,7 +36,7 @@ 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
@@ -180,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)
@@ -207,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 e7abb4690d..af5593e3ca 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/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt
index e58bc1e666..57a0768c88 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
@@ -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))
}
}
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 39326a1525..021dc09932 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
@@ -31,7 +31,6 @@ 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
@@ -57,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.profile?.getSpecificProfile(binding.profileList.text.toString())?.let {
binding.basalGraph.show(ProfileSealed.Pure(it))
binding.icGraph.show(ProfileSealed.Pure(it))
binding.isfGraph.show(ProfileSealed.Pure(it))
@@ -138,56 +137,118 @@ 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()
+ if (selection in 0 until (binding.profileList.adapter?.count ?: -1)) binding.profileList.setSelection(selection)
+ }
+ )
}
+ } else {
+ localProfilePlugin.currentProfileIndex = position
+ build()
}
- })
- localProfilePlugin.profile?.getSpecificProfile(spinner?.selectedItem.toString())?.let {
+ }
+ localProfilePlugin.profile?.getSpecificProfile(binding.profileList.text.toString())?.let {
binding.basalGraph.show(ProfileSealed.Pure(it))
binding.icGraph.show(ProfileSealed.Pure(it))
binding.isfGraph.show(ProfileSealed.Pure(it))
@@ -209,8 +270,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()
}
@@ -219,8 +284,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)
@@ -248,8 +317,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()
}
@@ -297,7 +370,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
@@ -309,7 +382,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/utils/alertDialogs/PrefImportSummaryDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt
index b51e5960e6..46756397d5 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 a2c4e78e2f..0bfd1f72f9 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 0f08ccea8a..349dd597c7 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()
@@ -103,13 +103,13 @@ class TddCalculator @Inject constructor(
val tbr = iobCobCalculator.getTempBasalIncludingConvertedExtended(t)
val profile = profileFunction.getProfile(t) ?: continue
val absoluteRate = tbr?.convertedToAbsolute(t, profile) ?: profile.getBasal(t)
- tdd.basalAmount += absoluteRate / T.mins(5).msecs().toDouble() * calculationStep.toDouble()
+ tdd.basalAmount += absoluteRate / 60.0 * 5.0
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
// they are not included in TBRs
val eb = iobCobCalculator.getExtendedBolus(t)
val absoluteEbRate = eb?.rate ?: 0.0
- tdd.bolusAmount += absoluteEbRate / T.mins(5).msecs().toDouble() * calculationStep.toDouble()
+ tdd.bolusAmount += absoluteEbRate / 60.0 * 5.0
}
//result.put(midnight, tdd)
}
@@ -118,13 +118,60 @@ 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()
+ val endTime = dateUtil.now()
+ val tdd = TotalDailyDose(timestamp = startTime)
+ //val result = TotalDailyDose()
+ repository.getBolusesDataFromTimeToTime(startTime, endTime, true).blockingGet()
+ .filter { it.type != Bolus.Type.PRIMING }
+ .forEach { t ->
+ //val midnight = MidnightTime.calc(t.timestamp)
+ //val tdd = result[midnight] ?: TotalDailyDose(timestamp = midnight)
+ tdd.bolusAmount += t.amount
+ //result.put(midnight, tdd)
+ }
+ repository.getCarbsDataFromTimeToTimeExpanded(startTime, endTime, true).blockingGet().forEach { t ->
+ //val midnight = MidnightTime.calc(t.timestamp)
+ //val tdd = result[midnight] ?: TotalDailyDose(timestamp = midnight)
+ tdd.carbs += t.amount
+ //result.put(midnight, tdd)
+ }
+ val calculationStep = T.mins(5).msecs()
+ //val tempBasals = iobCobCalculator.getTempBasalIncludingConvertedExtendedForRange(startTime, endTime, calculationStep)
+ for (t in startTime until endTime step calculationStep) {
+
+ //val midnight = MidnightTime.calc(t)
+ //val tdd = result[midnight] ?: TotalDailyDose(timestamp = midnight)
+ val tbr = iobCobCalculator.getTempBasalIncludingConvertedExtended(t)
+ val profile = profileFunction.getProfile(t) ?: continue
+ val absoluteRate = tbr?.convertedToAbsolute(t, profile) ?: profile.getBasal(t)
+ tdd.basalAmount += absoluteRate / 60.0 * 5.0
+
+ if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
+ // they are not included in TBRs
+ val eb = iobCobCalculator.getExtendedBolus(t)
+ val absoluteEbRate = eb?.rate ?: 0.0
+ tdd.bolusAmount += absoluteEbRate / 60.0 * 5.0
+ }
+ //result.put(midnight, tdd)
+ }
+ //for (i in 0 until tdd.size()) {
+ //val tdd = result.valueAt(i)
+ 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
@@ -143,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/res/drawable/cb_background_tt.xml b/app/src/main/res/drawable/cb_background_tt.xml
new file mode 100644
index 0000000000..291b9f535e
--- /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 0000000000..2c3522ae2b
--- /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 905ade57d3..b6ef8ff973 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 07faa6ee4d..b1008a92da 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 7d43edb857..9e2810b77b 100644
--- a/app/src/main/res/layout/actions_fragment.xml
+++ b/app/src/main/res/layout/actions_fragment.xml
@@ -27,14 +27,14 @@
-
-