From 2523c8e15f5025d3f11b57f3c0d29fa01baf7eb5 Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Wed, 9 Mar 2022 18:58:44 +0100 Subject: [PATCH 01/17] Experimenting --- .../org/isoron/uhabits/core/models/Entry.kt | 2 +- .../screens/habits/show/views/HistoryCard.kt | 64 +++++++++++++------ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt index e7aefb8a6..7a5c50408 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt @@ -26,7 +26,7 @@ data class Entry( /** * Value indicating that the habit is not applicable for this timestamp. */ - const val SKIP = 3 + const val SKIP = -3 /** * Value indicating that the user has performed the habit at this timestamp. diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt index bd201e0c5..563da0403 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt @@ -60,19 +60,34 @@ class HistoryCardPresenter( val timestamp = Timestamp.fromLocalDate(date) screen.showFeedback() if (habit.isNumerical) { - val entries = habit.computedEntries - val oldValue = entries.get(timestamp).value - screen.showNumberPicker(oldValue / 1000.0, habit.unit) { newValue: Double -> - val thousands = (newValue * 1000).roundToInt() - commandRunner.run( - CreateRepetitionCommand( - habitList, - habit, - timestamp, - thousands, - ), - ) - } +// val entries = habit.computedEntries +// val oldValue = entries.get(timestamp).value +// screen.showNumberPicker(oldValue / 1000.0, habit.unit) { newValue: Double -> +// val thousands = (newValue * 1000).roundToInt() +// commandRunner.run( +// CreateRepetitionCommand( +// habitList, +// habit, +// timestamp, +// thousands, +// ), +// ) +// } + + val currentValue = habit.computedEntries.get(timestamp).value + val nextValue = Entry.nextToggleValue( + value = currentValue, + isSkipEnabled = preferences.isSkipEnabled, + areQuestionMarksEnabled = preferences.areQuestionMarksEnabled + ) + commandRunner.run( + CreateRepetitionCommand( + habitList, + habit, + timestamp, + nextValue, + ), + ) } else { val currentValue = habit.computedEntries.get(timestamp).value val nextValue = Entry.nextToggleValue( @@ -105,12 +120,25 @@ class HistoryCardPresenter( val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today val entries = habit.computedEntries.getByInterval(oldest, today) val series = if (habit.isNumerical) { +// entries.map { +// Entry(it.timestamp, max(0, it.value)) +// }.map { +// when (it.value) { +// 0 -> HistoryChart.Square.OFF +// else -> HistoryChart.Square.ON +// } +// } entries.map { - Entry(it.timestamp, max(0, it.value)) - }.map { - when (it.value) { - 0 -> HistoryChart.Square.OFF - else -> HistoryChart.Square.ON + when { + it.value == SKIP -> { + HistoryChart.Square.HATCHED + } + it.value > 0 -> { + HistoryChart.Square.ON + } + else -> { + HistoryChart.Square.OFF + } } } } else { From 5527fd3514fe67f8dab651322df08f54686c51bb Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Wed, 9 Mar 2022 19:44:38 +0100 Subject: [PATCH 02/17] Skip button on measurable dialog --- .../common/dialogs/NumberPickerFactory.kt | 6 +++ .../activities/common/views/FrequencyChart.kt | 6 ++- .../org/isoron/uhabits/core/models/Entry.kt | 2 +- .../screens/habits/show/views/HistoryCard.kt | 49 +++++-------------- 4 files changed, 24 insertions(+), 39 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index c531e758b..c1a01c24a 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -30,6 +30,7 @@ import android.widget.NumberPicker import android.widget.TextView import androidx.appcompat.app.AlertDialog import org.isoron.uhabits.R +import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior import org.isoron.uhabits.inject.ActivityContext import org.isoron.uhabits.utils.InterfaceUtils @@ -76,6 +77,11 @@ class NumberPickerFactory val v = picker.value + 0.05 * picker2.value callback.onNumberPicked(v) } + .setNegativeButton(R.string.skip_button) { _, _ -> + picker.clearFocus() + val v = Entry.SKIP.toDouble() / 1000 + callback.onNumberPicked(v) + } .setOnDismissListener { callback.onNumberPickerDismissed() } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt index c4bad1a96..9e1858aba 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt @@ -173,7 +173,8 @@ class FrequencyChart : ScrollableChart { rect[0f, 0f, baseSize.toFloat()] = baseSize.toFloat() rect.offset(prevRect!!.left, prevRect!!.top + baseSize * j) val i = localeWeekdayList[j] % 7 - if (values != null) drawMarker(canvas, rect, values[i]) + if (values != null) + drawMarker(canvas, rect, values[i]) rect.offset(0f, rowHeight) } drawFooter(canvas, rect, date) @@ -222,11 +223,12 @@ class FrequencyChart : ScrollableChart { } private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?) { + val valueCopy = value?.let { max(0, it) } val padding = rect!!.height() * 0.2f // maximal allowed mark radius val maxRadius = (rect.height() - 2 * padding) / 2.0f // the real mark radius is scaled down by a factor depending on the maximal frequency - val scale = 1.0f / maxFreq * value!! + val scale = 1.0f / maxFreq * valueCopy!! val radius = maxRadius * scale val colorIndex = min((colors.size - 1), ((colors.size - 1) * scale).roundToInt()) pGraph!!.color = colors[colorIndex] diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt index 7a5c50408..169aaed20 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt @@ -26,7 +26,7 @@ data class Entry( /** * Value indicating that the habit is not applicable for this timestamp. */ - const val SKIP = -3 + const val SKIP = -2 /** * Value indicating that the user has performed the habit at this timestamp. diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt index 563da0403..3774aa19d 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt @@ -60,34 +60,19 @@ class HistoryCardPresenter( val timestamp = Timestamp.fromLocalDate(date) screen.showFeedback() if (habit.isNumerical) { -// val entries = habit.computedEntries -// val oldValue = entries.get(timestamp).value -// screen.showNumberPicker(oldValue / 1000.0, habit.unit) { newValue: Double -> -// val thousands = (newValue * 1000).roundToInt() -// commandRunner.run( -// CreateRepetitionCommand( -// habitList, -// habit, -// timestamp, -// thousands, -// ), -// ) -// } - - val currentValue = habit.computedEntries.get(timestamp).value - val nextValue = Entry.nextToggleValue( - value = currentValue, - isSkipEnabled = preferences.isSkipEnabled, - areQuestionMarksEnabled = preferences.areQuestionMarksEnabled - ) - commandRunner.run( - CreateRepetitionCommand( - habitList, - habit, - timestamp, - nextValue, - ), - ) + val entries = habit.computedEntries + val oldValue = entries.get(timestamp).value + screen.showNumberPicker(oldValue / 1000.0, habit.unit) { newValue: Double -> + val thousands = (newValue * 1000).roundToInt() + commandRunner.run( + CreateRepetitionCommand( + habitList, + habit, + timestamp, + thousands, + ), + ) + } } else { val currentValue = habit.computedEntries.get(timestamp).value val nextValue = Entry.nextToggleValue( @@ -120,14 +105,6 @@ class HistoryCardPresenter( val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today val entries = habit.computedEntries.getByInterval(oldest, today) val series = if (habit.isNumerical) { -// entries.map { -// Entry(it.timestamp, max(0, it.value)) -// }.map { -// when (it.value) { -// 0 -> HistoryChart.Square.OFF -// else -> HistoryChart.Square.ON -// } -// } entries.map { when { it.value == SKIP -> { From dff0ed6c1121e3d5d42cea8a777c472c80ac104c Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Wed, 9 Mar 2022 20:21:04 +0100 Subject: [PATCH 03/17] Added a comment --- .../isoron/uhabits/activities/common/views/FrequencyChart.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt index 9e1858aba..93a97dc41 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt @@ -223,7 +223,9 @@ class FrequencyChart : ScrollableChart { } private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?) { + // value can be negative when the entry is skipped val valueCopy = value?.let { max(0, it) } + val padding = rect!!.height() * 0.2f // maximal allowed mark radius val maxRadius = (rect.height() - 2 * padding) / 2.0f From df795fd977b7184b87ec8a9d813ed06e14f9319d Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Wed, 9 Mar 2022 21:54:19 +0100 Subject: [PATCH 04/17] Showing skipped in habits view --- .../activities/common/dialogs/NumberPickerFactory.kt | 8 ++++++++ .../activities/habits/list/views/NumberButtonView.kt | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index c1a01c24a..e72ddba0b 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -21,14 +21,17 @@ package org.isoron.uhabits.activities.common.dialogs import android.content.Context import android.content.DialogInterface +import android.content.DialogInterface.BUTTON_NEGATIVE import android.text.InputFilter import android.view.LayoutInflater +import android.view.View import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE import android.view.inputmethod.EditorInfo import android.widget.EditText import android.widget.NumberPicker import android.widget.TextView import androidx.appcompat.app.AlertDialog +import org.isoron.uhabits.HabitsApplication import org.isoron.uhabits.R import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior @@ -88,6 +91,11 @@ class NumberPickerFactory .create() dialog.setOnShowListener { + val preferences = (context.applicationContext as HabitsApplication).component.preferences + if(!preferences.isSkipEnabled){ + dialog.getButton(BUTTON_NEGATIVE).visibility = View.GONE + } + picker.getChildAt(0)?.requestFocus() dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE) } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt index 3e48ed1a6..ecfac7b9a 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt @@ -29,6 +29,7 @@ import android.view.View import android.view.View.OnClickListener import android.view.View.OnLongClickListener import org.isoron.uhabits.R +import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.inject.ActivityContext import org.isoron.uhabits.utils.InterfaceUtils.getDimension @@ -132,6 +133,12 @@ class NumberButtonView( private val lowContrast: Int private val mediumContrast: Int + private val paint = TextPaint().apply { + typeface = getFontAwesome() + isAntiAlias = true + textAlign = Paint.Align.CENTER + } + private val pUnit: TextPaint = TextPaint().apply { textSize = getDimension(context, R.dimen.smallerTextSize) typeface = NORMAL_TYPEFACE @@ -169,6 +176,11 @@ class NumberButtonView( typeface = BOLD_TYPEFACE textSize = dim(R.dimen.smallTextSize) } + value == Entry.SKIP.toDouble() / 1000 ->{ + label = resources.getString(R.string.fa_skipped) + textSize = dim(R.dimen.smallTextSize) + typeface = getFontAwesome() + } preferences.areQuestionMarksEnabled -> { label = resources.getString(R.string.fa_question) typeface = getFontAwesome() From 606536695ff1aa41eb303904a53c8daaf3307826 Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Wed, 9 Mar 2022 22:34:19 +0100 Subject: [PATCH 05/17] Corrected bugs after base repo merge --- .../uhabits/activities/common/dialogs/NumberPickerFactory.kt | 5 +++-- uhabits-android/src/main/res/values/strings.xml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index e0e1ab37c..0eb44fc21 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -108,10 +108,11 @@ class NumberPickerFactory .setNegativeButton(android.R.string.cancel) { _, _ -> callback.onNumberPickerDismissed() } - .setNegativeButton(R.string.skip_button) { _, _ -> + .setNegativeButton(R.string.skip_day) { _, _ -> picker.clearFocus() val v = Entry.SKIP.toDouble() / 1000 - callback.onNumberPicked(v) + val note = etNotes.text.toString() + callback.onNumberPicked(v, note) } .setOnDismissListener { callback.onNumberPickerDismissed() diff --git a/uhabits-android/src/main/res/values/strings.xml b/uhabits-android/src/main/res/values/strings.xml index 75d7d88a7..9fec1cbca 100644 --- a/uhabits-android/src/main/res/values/strings.xml +++ b/uhabits-android/src/main/res/values/strings.xml @@ -225,6 +225,7 @@ Increment Decrement Enable skip days + Skip Toggle twice to add a skip instead of a checkmark. Skips keep your score unchanged and don\'t break your streak. Show question marks for missing data Differentiate days without data from actual lapses. To enter a lapse, toggle twice. From d88df53679fe5da334f36ccadbc3f211b0e1468c Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Wed, 9 Mar 2022 23:02:23 +0100 Subject: [PATCH 06/17] Formatted some code --- .../uhabits/activities/common/dialogs/NumberPickerFactory.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index 0eb44fc21..cc521bd77 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -120,8 +120,9 @@ class NumberPickerFactory .create() dialog.setOnShowListener { - val preferences = (context.applicationContext as HabitsApplication).component.preferences - if(!preferences.isSkipEnabled){ + val preferences = + (context.applicationContext as HabitsApplication).component.preferences + if (!preferences.isSkipEnabled) { dialog.getButton(BUTTON_NEGATIVE).visibility = View.GONE } showSoftInput(dialog, pickerInputText) From b75d5eeb349acdf2d95a71425b071122d4284297 Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Wed, 9 Mar 2022 23:03:59 +0100 Subject: [PATCH 07/17] Formatted some more code --- .../uhabits/activities/habits/list/views/NumberButtonView.kt | 2 +- .../uhabits/core/ui/screens/habits/show/views/HistoryCard.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt index 21ad60bfa..e7eccfcce 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt @@ -188,7 +188,7 @@ class NumberButtonView( typeface = BOLD_TYPEFACE textSize = dim(R.dimen.smallTextSize) } - value == Entry.SKIP.toDouble() / 1000 ->{ + value == Entry.SKIP.toDouble() / 1000 -> { label = resources.getString(R.string.fa_skipped) textSize = dim(R.dimen.smallTextSize) typeface = getFontAwesome() diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt index 98b4cb54d..aca6c3811 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt @@ -203,6 +203,7 @@ class HistoryCardPresenter( dateString: String, callback: ListHabitsBehavior.NumberPickerCallback, ) + fun showCheckmarkDialog( selectedValue: Int, notes: String, From 3b650d63b4b9dfd17cdbd267f2150c0c19e175b2 Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Sun, 20 Mar 2022 16:58:49 +0100 Subject: [PATCH 08/17] Got the score chart to work --- .../isoron/uhabits/core/models/ScoreList.kt | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt index 23bd88fd1..74a66342e 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt @@ -100,20 +100,25 @@ class ScoreList { } val normalizedRollingSum = rollingSum / 1000 - val percentageCompleted = if (!isAtMost) { - if (targetValue > 0) - min(1.0, normalizedRollingSum / targetValue) - else - 1.0 - } else { - if (targetValue > 0) { - (1 - ((normalizedRollingSum - targetValue) / targetValue)).coerceIn(0.0, 1.0) + if(values[offset] != Entry.SKIP) { + val percentageCompleted = if (!isAtMost) { + if (targetValue > 0) + min(1.0, normalizedRollingSum / targetValue) + else + 1.0 } else { - if (normalizedRollingSum > 0) 0.0 else 1.0 + if (targetValue > 0) { + (1 - ((normalizedRollingSum - targetValue) / targetValue)).coerceIn( + 0.0, + 1.0 + ) + } else { + if (normalizedRollingSum > 0) 0.0 else 1.0 + } } - } - previousValue = compute(freq, previousValue, percentageCompleted) + previousValue = compute(freq, previousValue, percentageCompleted) + } } else { if (values[offset] == Entry.YES_MANUAL) { rollingSum += 1.0 From c2731c03c381743185b18e9fa75b1450b4bfcedc Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Sun, 20 Mar 2022 17:06:57 +0100 Subject: [PATCH 09/17] Resolving hiqua's comments --- .../java/org/isoron/uhabits/core/models/Entry.kt | 2 +- .../ui/screens/habits/show/views/HistoryCard.kt | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt index 226ddafc1..88d0593ef 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Entry.kt @@ -27,7 +27,7 @@ data class Entry( /** * Value indicating that the habit is not applicable for this timestamp. */ - const val SKIP = -2 + const val SKIP = 3 /** * Value indicating that the user has performed the habit at this timestamp. diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt index aca6c3811..a5b65fdc9 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt @@ -153,15 +153,11 @@ class HistoryCardPresenter( val series = if (habit.isNumerical) { entries.map { when { - it.value == SKIP -> { - HistoryChart.Square.HATCHED - } - it.value > 0 -> { - HistoryChart.Square.ON - } - else -> { - HistoryChart.Square.OFF - } + it.value == Entry.UNKNOWN -> OFF + it.value == SKIP -> HATCHED + (habit.targetType == AT_MOST) && (it.value / 1000.0 <= habit.targetValue) -> ON + (habit.targetType == AT_LEAST) && (it.value / 1000.0 >= habit.targetValue) -> ON + else -> GREY } } } else { From 9f52a7af66d4211dc50d156fb7f0339a1b94fc8a Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Sun, 20 Mar 2022 17:30:48 +0100 Subject: [PATCH 10/17] Disabled SKIP for non-daily numerical habits --- .../common/dialogs/NumberPickerFactory.kt | 20 +++++++++++++------ .../habits/list/ListHabitsScreen.kt | 4 +++- .../habits/show/ShowHabitActivity.kt | 6 ++++-- .../NumericalCheckmarkWidgetActivity.kt | 1 + .../screens/habits/list/ListHabitsBehavior.kt | 8 +++----- .../screens/habits/show/views/HistoryCard.kt | 10 ++++------ 6 files changed, 29 insertions(+), 20 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index cc521bd77..e77c41070 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -37,6 +37,8 @@ import androidx.appcompat.app.AlertDialog import org.isoron.uhabits.HabitsApplication import org.isoron.uhabits.R import org.isoron.uhabits.core.models.Entry +import org.isoron.uhabits.core.models.Frequency +import org.isoron.uhabits.core.models.Frequency.Companion.DAILY import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior import org.isoron.uhabits.inject.ActivityContext import org.isoron.uhabits.utils.InterfaceUtils @@ -55,6 +57,7 @@ class NumberPickerFactory unit: String, notes: String, dateString: String, + frequency: Frequency, callback: ListHabitsBehavior.NumberPickerCallback ): AlertDialog { val inflater = LayoutInflater.from(context) @@ -95,7 +98,7 @@ class NumberPickerFactory picker2.value = intValue % 100 etNotes.setText(notes) - val dialog = AlertDialog.Builder(context) + val dialogBuilder = AlertDialog.Builder(context) .setView(view) .setTitle(dateString) .setPositiveButton(R.string.save) { _, _ -> @@ -108,16 +111,21 @@ class NumberPickerFactory .setNegativeButton(android.R.string.cancel) { _, _ -> callback.onNumberPickerDismissed() } - .setNegativeButton(R.string.skip_day) { _, _ -> + .setOnDismissListener { + callback.onNumberPickerDismissed() + } + + + if(frequency == DAILY){ + dialogBuilder.setNegativeButton(R.string.skip_day) { _, _ -> picker.clearFocus() val v = Entry.SKIP.toDouble() / 1000 val note = etNotes.text.toString() callback.onNumberPicked(v, note) } - .setOnDismissListener { - callback.onNumberPickerDismissed() - } - .create() + } + + val dialog = dialogBuilder.create() dialog.setOnShowListener { val preferences = diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt index 774e5345f..4ec28f9f3 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt @@ -40,6 +40,7 @@ import org.isoron.uhabits.core.commands.CreateHabitCommand import org.isoron.uhabits.core.commands.DeleteHabitsCommand import org.isoron.uhabits.core.commands.EditHabitCommand import org.isoron.uhabits.core.commands.UnarchiveHabitsCommand +import org.isoron.uhabits.core.models.Frequency import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.tasks.TaskRunner @@ -230,9 +231,10 @@ class ListHabitsScreen unit: String, notes: String, dateString: String, + frequency: Frequency, callback: ListHabitsBehavior.NumberPickerCallback ) { - numberPickerFactory.create(value, unit, notes, dateString, callback).show() + numberPickerFactory.create(value, unit, notes, dateString, frequency, callback).show() } override fun showCheckmarkDialog( diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt index 0b696ad4e..4af6948ad 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt @@ -39,6 +39,7 @@ import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory import org.isoron.uhabits.core.commands.Command import org.isoron.uhabits.core.commands.CommandRunner +import org.isoron.uhabits.core.models.Frequency import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.preferences.Preferences @@ -169,9 +170,10 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { unit: String, notes: String, dateString: String, - callback: ListHabitsBehavior.NumberPickerCallback, + frequency: Frequency, + callback: ListHabitsBehavior.NumberPickerCallback ) { - NumberPickerFactory(this@ShowHabitActivity).create(value, unit, notes, dateString, callback).show() + NumberPickerFactory(this@ShowHabitActivity).create(value, unit, notes, dateString, frequency, callback).show() } override fun showCheckmarkDialog( diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/NumericalCheckmarkWidgetActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/NumericalCheckmarkWidgetActivity.kt index c557ed942..b7bc7f229 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/NumericalCheckmarkWidgetActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/NumericalCheckmarkWidgetActivity.kt @@ -81,6 +81,7 @@ class NumericalCheckmarkWidgetActivity : Activity(), ListHabitsBehavior.NumberPi data.habit.unit, entry.notes, today.toDialogDateString(), + data.habit.frequency, this ).show() } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt index d39227927..e93c8f220 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt @@ -21,11 +21,7 @@ package org.isoron.uhabits.core.ui.screens.habits.list import org.isoron.platform.time.LocalDate import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CreateRepetitionCommand -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.HabitList -import org.isoron.uhabits.core.models.HabitType -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.models.Timestamp +import org.isoron.uhabits.core.models.* import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.tasks.ExportCSVTask import org.isoron.uhabits.core.tasks.TaskRunner @@ -58,6 +54,7 @@ open class ListHabitsBehavior @Inject constructor( habit.unit, entry.notes, timestamp.toDialogDateString(), + habit.frequency ) { newValue: Double, newNotes: String, -> val value = (newValue * 1000).roundToInt() commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, value, newNotes)) @@ -167,6 +164,7 @@ open class ListHabitsBehavior @Inject constructor( unit: String, notes: String, dateString: String, + frequency: Frequency, callback: NumberPickerCallback ) fun showCheckmarkDialog( diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt index a5b65fdc9..e9ac10a52 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt @@ -23,16 +23,12 @@ import org.isoron.platform.time.DayOfWeek import org.isoron.platform.time.LocalDate import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CreateRepetitionCommand -import org.isoron.uhabits.core.models.Entry +import org.isoron.uhabits.core.models.* import org.isoron.uhabits.core.models.Entry.Companion.SKIP import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.NumericalHabitType.AT_LEAST import org.isoron.uhabits.core.models.NumericalHabitType.AT_MOST -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior import org.isoron.uhabits.core.ui.views.HistoryChart @@ -123,6 +119,7 @@ class HistoryCardPresenter( habit.unit, entry.notes, timestamp.toDialogDateString(), + frequency = habit.frequency ) { newValue: Double, newNotes: String -> val thousands = (newValue * 1000).roundToInt() commandRunner.run( @@ -197,7 +194,8 @@ class HistoryCardPresenter( unit: String, notes: String, dateString: String, - callback: ListHabitsBehavior.NumberPickerCallback, + frequency: Frequency, + callback: ListHabitsBehavior.NumberPickerCallback ) fun showCheckmarkDialog( From fb46799cbb7f813a7966e1afe364722e3ab5d471 Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Sun, 20 Mar 2022 18:33:04 +0100 Subject: [PATCH 11/17] SKIP affecting target chart --- .../isoron/uhabits/core/models/EntryList.kt | 33 +++++++++++++++- .../screens/habits/show/views/TargetCard.kt | 38 ++++++++++++++++--- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt index 5212923b1..e3596eecb 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt @@ -276,6 +276,8 @@ open class EntryList { * For numerical habits, non-positive entry values are converted to zero. For boolean habits, each * YES_MANUAL value is converted to 1000 and all other values are converted to zero. * + * SKIP values are treated converted to zero (if they weren't, each SKIP day would count as 0.003). + * * The returned list is sorted by timestamp, with the newest entry coming first and the oldest entry * coming last. If the original list has gaps in it (for example, weeks or months without any * entries), then the list produced by this method will also have gaps. @@ -289,7 +291,10 @@ fun List.groupedSum( ): List { return this.map { (timestamp, value) -> if (isNumerical) { - Entry(timestamp, max(0, value)) + if(value == SKIP) + Entry(timestamp, 0) + else + Entry(timestamp, max(0, value)) } else { Entry(timestamp, if (value == YES_MANUAL) 1000 else 0) } @@ -304,3 +309,29 @@ fun List.groupedSum( - timestamp.unixTime } } + +/** + * Counts the number of days with vaLue SKIP in the given perio + */ +fun List.countSkippedDays( + truncateField: DateUtils.TruncateField, + firstWeekday: Int = Calendar.SATURDAY, + isNumerical: Boolean, +): List { + return this.map { (timestamp, value) -> + if (value == SKIP) { + Entry(timestamp, 1) + } else { + Entry(timestamp, 0) + } + }.groupBy { entry -> + entry.timestamp.truncate( + truncateField, + firstWeekday, + ) + }.entries.map { (timestamp, entries) -> + Entry(timestamp, entries.sumOf { it.value }) + }.sortedBy { (timestamp, _) -> + - timestamp.unixTime + } +} diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt index 15d500def..0c53a290d 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt @@ -21,6 +21,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.countSkippedDays import org.isoron.uhabits.core.models.groupedSum import org.isoron.uhabits.core.ui.views.Theme import org.isoron.uhabits.core.utils.DateUtils @@ -51,37 +52,64 @@ class TargetCardPresenter { isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDayToday = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.DAY, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val valueThisWeek = entries.groupedSum( truncateField = DateUtils.TruncateField.WEEK_NUMBER, firstWeekday = firstWeekday, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDaysThisWeek = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.WEEK_NUMBER, + firstWeekday = firstWeekday, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val valueThisMonth = entries.groupedSum( truncateField = DateUtils.TruncateField.MONTH, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDaysThisMonth = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.MONTH, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val valueThisQuarter = entries.groupedSum( truncateField = DateUtils.TruncateField.QUARTER, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDaysThisQuarter = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.QUARTER, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val valueThisYear = entries.groupedSum( truncateField = DateUtils.TruncateField.YEAR, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDaysThisYear = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.YEAR, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val cal = DateUtils.getStartOfTodayCalendarWithOffset() val daysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH) val daysInQuarter = 91 val daysInYear = cal.getActualMaximum(Calendar.DAY_OF_YEAR) - val targetToday = habit.targetValue / habit.frequency.denominator - val targetThisWeek = targetToday * 7 - val targetThisMonth = targetToday * daysInMonth - val targetThisQuarter = targetToday * daysInQuarter - val targetThisYear = targetToday * daysInYear + val dailyTarget = habit.targetValue / habit.frequency.denominator + val targetToday = dailyTarget * (1 - skippedDayToday) + val targetThisWeek = dailyTarget * (7 - skippedDaysThisWeek) + val targetThisMonth = dailyTarget * (daysInMonth - skippedDaysThisMonth) + val targetThisQuarter = dailyTarget * (daysInQuarter - skippedDaysThisQuarter) + val targetThisYear = dailyTarget * (daysInYear - skippedDaysThisYear) val values = ArrayList() if (habit.frequency.denominator <= 1) values.add(valueToday / 1e3) From 6facce10f5e7cd2a5d4d8707abedb35191ad43d3 Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Sun, 20 Mar 2022 19:08:47 +0100 Subject: [PATCH 12/17] fuck --- .../uhabits/activities/common/dialogs/NumberPickerFactory.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index e77c41070..4b450ec9a 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -115,7 +115,6 @@ class NumberPickerFactory callback.onNumberPickerDismissed() } - if(frequency == DAILY){ dialogBuilder.setNegativeButton(R.string.skip_day) { _, _ -> picker.clearFocus() From 2c5322439b6d13c7005edeeeb624613767c49d38 Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Sun, 20 Mar 2022 19:22:21 +0100 Subject: [PATCH 13/17] Code style fixes --- .../activities/common/dialogs/NumberPickerFactory.kt | 2 +- .../java/org/isoron/uhabits/core/models/EntryList.kt | 6 +++--- .../java/org/isoron/uhabits/core/models/ScoreList.kt | 2 +- .../core/ui/screens/habits/list/ListHabitsBehavior.kt | 7 ++++++- .../core/ui/screens/habits/show/views/HistoryCard.kt | 7 ++++++- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index 4b450ec9a..b7f47074d 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -115,7 +115,7 @@ class NumberPickerFactory callback.onNumberPickerDismissed() } - if(frequency == DAILY){ + if (frequency == DAILY) { dialogBuilder.setNegativeButton(R.string.skip_day) { _, _ -> picker.clearFocus() val v = Entry.SKIP.toDouble() / 1000 diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt index e3596eecb..3c6a1ed58 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt @@ -291,7 +291,7 @@ fun List.groupedSum( ): List { return this.map { (timestamp, value) -> if (isNumerical) { - if(value == SKIP) + if (value == SKIP) Entry(timestamp, 0) else Entry(timestamp, max(0, value)) @@ -306,7 +306,7 @@ fun List.groupedSum( }.entries.map { (timestamp, entries) -> Entry(timestamp, entries.sumOf { it.value }) }.sortedBy { (timestamp, _) -> - - timestamp.unixTime + -timestamp.unixTime } } @@ -332,6 +332,6 @@ fun List.countSkippedDays( }.entries.map { (timestamp, entries) -> Entry(timestamp, entries.sumOf { it.value }) }.sortedBy { (timestamp, _) -> - - timestamp.unixTime + -timestamp.unixTime } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt index 74a66342e..385ee0f0c 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt @@ -100,7 +100,7 @@ class ScoreList { } val normalizedRollingSum = rollingSum / 1000 - if(values[offset] != Entry.SKIP) { + if (values[offset] != Entry.SKIP) { val percentageCompleted = if (!isAtMost) { if (targetValue > 0) min(1.0, normalizedRollingSum / targetValue) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt index e93c8f220..278d904db 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt @@ -21,7 +21,12 @@ package org.isoron.uhabits.core.ui.screens.habits.list import org.isoron.platform.time.LocalDate import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CreateRepetitionCommand -import org.isoron.uhabits.core.models.* +import org.isoron.uhabits.core.models.Frequency +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitList +import org.isoron.uhabits.core.models.HabitType +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.tasks.ExportCSVTask import org.isoron.uhabits.core.tasks.TaskRunner diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt index e9ac10a52..97d149906 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt @@ -23,12 +23,17 @@ import org.isoron.platform.time.DayOfWeek import org.isoron.platform.time.LocalDate import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CreateRepetitionCommand -import org.isoron.uhabits.core.models.* +import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.models.Entry.Companion.SKIP import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL +import org.isoron.uhabits.core.models.Frequency +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.NumericalHabitType.AT_LEAST import org.isoron.uhabits.core.models.NumericalHabitType.AT_MOST +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior import org.isoron.uhabits.core.ui.views.HistoryChart From 6cf910237856cecd0d9bc4695d02052c5a12f2fa Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Sun, 20 Mar 2022 19:37:48 +0100 Subject: [PATCH 14/17] Corrected failing test --- .../core/ui/screens/habits/list/ListHabitsBehaviorTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt index 8192583ad..0ecd796f2 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt @@ -79,7 +79,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() { @Test fun testOnEdit() { behavior.onEdit(habit2, getToday()) - verify(screen).showNumberPicker(eq(0.1), eq("miles"), eq(""), eq("Jan 25, 2015"), picker.capture()) + verify(screen).showNumberPicker(eq(0.1), eq("miles"), eq(""), eq("Jan 25, 2015"), habit2.frequency, picker.capture()) picker.lastValue.onNumberPicked(100.0, "") val today = getTodayWithOffset() assertThat(habit2.computedEntries.get(today).value, equalTo(100000)) From 9ee9427bfd4981c0c5876a438239dd9b3f0ac58c Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Sun, 20 Mar 2022 20:08:56 +0100 Subject: [PATCH 15/17] Fixed the test properly --- .../ui/screens/habits/list/ListHabitsBehaviorTest.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt index 0ecd796f2..4df4a28f3 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt @@ -33,6 +33,7 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.core.IsEqual.equalTo import org.isoron.uhabits.core.BaseUnitTest import org.isoron.uhabits.core.models.Entry +import org.isoron.uhabits.core.models.Frequency import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday @@ -79,7 +80,14 @@ class ListHabitsBehaviorTest : BaseUnitTest() { @Test fun testOnEdit() { behavior.onEdit(habit2, getToday()) - verify(screen).showNumberPicker(eq(0.1), eq("miles"), eq(""), eq("Jan 25, 2015"), habit2.frequency, picker.capture()) + verify(screen).showNumberPicker( + eq(0.1), + eq("miles"), + eq(""), + eq("Jan 25, 2015"), + eq(Frequency.DAILY), + picker.capture() + ) picker.lastValue.onNumberPicked(100.0, "") val today = getTodayWithOffset() assertThat(habit2.computedEntries.get(today).value, equalTo(100000)) From 5d25b236f7959e131ff1be4c3fcbb57d5db46a48 Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Tue, 22 Mar 2022 17:05:06 +0100 Subject: [PATCH 16/17] Resolving comments --- .../habits/list/views/NumberButtonView.kt | 10 +++++----- .../org/isoron/uhabits/core/models/EntryList.kt | 7 +++---- .../core/ui/screens/habits/show/views/TargetCard.kt | 13 ++++--------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt index e7eccfcce..ed00049ee 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt @@ -183,16 +183,16 @@ class NumberButtonView( val textSize: Float when { - value >= 0 -> { - label = value.toShortString() - typeface = BOLD_TYPEFACE - textSize = dim(R.dimen.smallTextSize) - } value == Entry.SKIP.toDouble() / 1000 -> { label = resources.getString(R.string.fa_skipped) textSize = dim(R.dimen.smallTextSize) typeface = getFontAwesome() } + value >= 0 -> { + label = value.toShortString() + typeface = BOLD_TYPEFACE + textSize = dim(R.dimen.smallTextSize) + } preferences.areQuestionMarksEnabled -> { label = resources.getString(R.string.fa_question) typeface = getFontAwesome() diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt index 3c6a1ed58..2d9662b21 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt @@ -276,7 +276,7 @@ open class EntryList { * For numerical habits, non-positive entry values are converted to zero. For boolean habits, each * YES_MANUAL value is converted to 1000 and all other values are converted to zero. * - * SKIP values are treated converted to zero (if they weren't, each SKIP day would count as 0.003). + * SKIP values are converted to zero (if they weren't, each SKIP day would count as 0.003). * * The returned list is sorted by timestamp, with the newest entry coming first and the oldest entry * coming last. If the original list has gaps in it (for example, weeks or months without any @@ -311,12 +311,11 @@ fun List.groupedSum( } /** - * Counts the number of days with vaLue SKIP in the given perio + * Counts the number of days with vaLue SKIP in the given period. */ fun List.countSkippedDays( truncateField: DateUtils.TruncateField, - firstWeekday: Int = Calendar.SATURDAY, - isNumerical: Boolean, + firstWeekday: Int = Calendar.SATURDAY ): List { return this.map { (timestamp, value) -> if (value == SKIP) { diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt index 0c53a290d..9b8add0dc 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt @@ -53,8 +53,7 @@ class TargetCardPresenter { ).firstOrNull()?.value ?: 0 val skippedDayToday = entries.countSkippedDays( - truncateField = DateUtils.TruncateField.DAY, - isNumerical = habit.isNumerical + truncateField = DateUtils.TruncateField.DAY ).firstOrNull()?.value ?: 0 val valueThisWeek = entries.groupedSum( @@ -65,8 +64,7 @@ class TargetCardPresenter { val skippedDaysThisWeek = entries.countSkippedDays( truncateField = DateUtils.TruncateField.WEEK_NUMBER, - firstWeekday = firstWeekday, - isNumerical = habit.isNumerical + firstWeekday = firstWeekday ).firstOrNull()?.value ?: 0 val valueThisMonth = entries.groupedSum( @@ -76,7 +74,6 @@ class TargetCardPresenter { val skippedDaysThisMonth = entries.countSkippedDays( truncateField = DateUtils.TruncateField.MONTH, - isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 val valueThisQuarter = entries.groupedSum( @@ -85,8 +82,7 @@ class TargetCardPresenter { ).firstOrNull()?.value ?: 0 val skippedDaysThisQuarter = entries.countSkippedDays( - truncateField = DateUtils.TruncateField.QUARTER, - isNumerical = habit.isNumerical + truncateField = DateUtils.TruncateField.QUARTER ).firstOrNull()?.value ?: 0 val valueThisYear = entries.groupedSum( @@ -95,8 +91,7 @@ class TargetCardPresenter { ).firstOrNull()?.value ?: 0 val skippedDaysThisYear = entries.countSkippedDays( - truncateField = DateUtils.TruncateField.YEAR, - isNumerical = habit.isNumerical + truncateField = DateUtils.TruncateField.YEAR ).firstOrNull()?.value ?: 0 val cal = DateUtils.getStartOfTodayCalendarWithOffset() From 6249a76207be88f853267e08b3f7b71d684ec70a Mon Sep 17 00:00:00 2001 From: Jakub Kalinowski Date: Tue, 22 Mar 2022 22:49:49 +0100 Subject: [PATCH 17/17] Added test for skipped days --- .../uhabits/core/models/ScoreListTest.kt | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/ScoreListTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/ScoreListTest.kt index 6ca3e2806..a152bdfe0 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/ScoreListTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/ScoreListTest.kt @@ -23,6 +23,7 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.number.IsCloseTo import org.hamcrest.number.OrderingComparison import org.isoron.uhabits.core.BaseUnitTest +import org.isoron.uhabits.core.models.Entry.Companion.SKIP import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday import org.junit.Before import org.junit.Test @@ -381,6 +382,66 @@ class NumericalAtLeastScoreListTest : NumericalScoreListTest() { } } +class NumericalAtLeastScoreListWithSkipTest : NumericalScoreListTest() { + @Before + @Throws(Exception::class) + override fun setUp() { + super.setUp() + habit = fixtures.createEmptyNumericalHabit(NumericalHabitType.AT_LEAST) + } + + @Test + fun test_getValue() { + addEntries(0, 10, 2000) + addEntries(10, 11, SKIP) + addEntries(11, 15, 2000) + addEntries(15, 16, SKIP) + addEntries(16, 20, 2000) + val expectedValues = doubleArrayOf( + 0.617008, + 0.596033, + 0.573910, + 0.550574, + 0.525961, + 0.500000, + 0.472617, + 0.443734, + 0.413270, + 0.381137, + 0.347244, // skipped day should have the same score as the previous day + 0.347244, + 0.311495, + 0.273788, + 0.234017, + 0.192067, // skipped day should have the same score as the previous day + 0.192067, + 0.147820, + 0.101149, + 0.051922, + 0.000000, + 0.000000, + 0.000000 + ) + checkScoreValues(expectedValues) + } + + @Test + fun skipsShouldNotAffectScore() { + addEntries(0, 500, 1000) + val initialScore = habit.scores[today].value + + addEntries(500, 1000, SKIP) + assertThat(habit.scores[today].value, IsCloseTo.closeTo(initialScore, E)) + + addEntries(0, 300, 1000) + addEntries(300, 500, SKIP) + addEntries(500, 700, 1000) + + // skipped days should be treated as if they never existed + assertThat(habit.scores[today].value, IsCloseTo.closeTo(initialScore, E)) + } +} + class NumericalAtMostScoreListTest : NumericalScoreListTest() { @Before @Throws(Exception::class)