diff --git a/uhabits-android/src/androidTest/assets/views/habits/list/NumberButtonView/render_at_most_above.png b/uhabits-android/src/androidTest/assets/views/habits/list/NumberButtonView/render_at_most_above.png index 5bd20a34f..4cea80498 100644 Binary files a/uhabits-android/src/androidTest/assets/views/habits/list/NumberButtonView/render_at_most_above.png and b/uhabits-android/src/androidTest/assets/views/habits/list/NumberButtonView/render_at_most_above.png differ diff --git a/uhabits-android/src/androidTest/assets/views/habits/list/NumberButtonView/render_zero.png b/uhabits-android/src/androidTest/assets/views/habits/list/NumberButtonView/render_zero.png index 5f6bf8c65..a405c0092 100644 Binary files a/uhabits-android/src/androidTest/assets/views/habits/list/NumberButtonView/render_zero.png and b/uhabits-android/src/androidTest/assets/views/habits/list/NumberButtonView/render_zero.png differ diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.kt index fcad26125..5edae637f 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.kt @@ -62,6 +62,7 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener { firstWeekday = preferences.firstWeekday, paletteColor = habit.color, series = emptyList(), + defaultSquare = HistoryChart.Square.OFF, theme = themeSwitcher.currentTheme, today = DateUtils.getTodayWithOffset().toLocalDate(), onDateClickedListener = onDateClickedListener ?: OnDateClickedListener { }, @@ -101,6 +102,7 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener { theme = LightTheme() ) chart?.series = model.series + chart?.defaultSquare = model.defaultSquare dataView.postInvalidate() } 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 08685026d..405bf606d 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 @@ -162,14 +162,14 @@ class NumberButtonView( fun draw(canvas: Canvas) { var activeColor = if (targetType == NumericalHabitType.AT_LEAST) { when { + value < 0.0 && preferences.areQuestionMarksEnabled -> lowContrast max(0.0, value) >= threshold -> color - value <= 0 -> lowContrast else -> mediumContrast } } else { when { + value < 0.0 && preferences.areQuestionMarksEnabled -> lowContrast value <= threshold -> color - value >= 2 * threshold -> lowContrast else -> mediumContrast } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardView.kt index c60e84bd1..f429e1718 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardView.kt @@ -43,6 +43,7 @@ class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(cont theme = state.theme, dateFormatter = JavaLocalDateFormatter(Locale.getDefault()), series = state.series, + defaultSquare = state.defaultSquare, firstWeekday = state.firstWeekday, ) binding.chart.postInvalidate() diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt index 2e26a487b..c8d4dce44 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt @@ -56,7 +56,9 @@ class HistoryWidget( theme = WidgetTheme(), ) (widgetView.dataView as AndroidDataView).apply { - (this.view as HistoryChart).series = model.series + val historyChart = (this.view as HistoryChart) + historyChart.series = model.series + historyChart.defaultSquare = model.defaultSquare } } @@ -71,6 +73,7 @@ class HistoryWidget( dateFormatter = JavaLocalDateFormatter(Locale.getDefault()), firstWeekday = prefs.firstWeekday, series = listOf(), + defaultSquare = HistoryChart.Square.OFF, ) } ).apply { diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/commands/CreateHabitCommand.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/commands/CreateHabitCommand.kt index 49e3bae7a..8ff21f407 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/commands/CreateHabitCommand.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/commands/CreateHabitCommand.kt @@ -31,5 +31,6 @@ data class CreateHabitCommand( val habit = modelFactory.buildHabit() habit.copyFrom(model) habitList.add(habit) + habit.recompute() } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt index a2dd13846..5134d45b0 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt @@ -90,9 +90,10 @@ data class Habit( isNumerical = isNumerical, ) - val to = DateUtils.getTodayWithOffset().plus(30) + val today = DateUtils.getTodayWithOffset() + val to = today.plus(30) val entries = computedEntries.getKnown() - var from = entries.lastOrNull()?.timestamp ?: to + var from = entries.lastOrNull()?.timestamp ?: today if (from.isNewerThan(to)) from = to scores.recompute( 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 a062f7ede..23bd88fd1 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 @@ -75,13 +75,12 @@ class ScoreList { to: Timestamp, ) { map.clear() - if (computedEntries.getKnown().isEmpty()) return - if (from.isNewerThan(to)) return var rollingSum = 0.0 var numerator = frequency.numerator var denominator = frequency.denominator val freq = frequency.toDouble() val values = computedEntries.getByInterval(from, to).map { it.value }.toIntArray() + val isAtMost = numericalHabitType == NumericalHabitType.AT_MOST // For non-daily boolean habits, we double the numerator and the denominator to smooth // out irregular repetition schedules (for example, weekly habits performed on different @@ -91,7 +90,7 @@ class ScoreList { denominator *= 2 } - var previousValue = 0.0 + var previousValue = if (isNumerical && isAtMost) 1.0 else 0.0 for (i in values.indices) { val offset = values.size - i - 1 if (isNumerical) { @@ -101,7 +100,7 @@ class ScoreList { } val normalizedRollingSum = rollingSum / 1000 - val percentageCompleted = if (numericalHabitType == NumericalHabitType.AT_LEAST) { + val percentageCompleted = if (!isAtMost) { if (targetValue > 0) min(1.0, normalizedRollingSum / targetValue) else 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 8798f027d..dcc7cba24 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 @@ -45,6 +45,7 @@ data class HistoryCardState( val color: PaletteColor, val firstWeekday: DayOfWeek, val series: List, + val defaultSquare: HistoryChart.Square, val theme: Theme, val today: LocalDate, ) @@ -131,6 +132,10 @@ class HistoryCardPresenter( } } } + val defaultSquare = if (habit.isNumerical && habit.targetType == NumericalHabitType.AT_MOST) + HistoryChart.Square.ON + else + HistoryChart.Square.OFF return HistoryCardState( color = habit.color, @@ -138,6 +143,7 @@ class HistoryCardPresenter( today = today.toLocalDate(), theme = theme, series = series, + defaultSquare = defaultSquare ) } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/views/HistoryChart.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/views/HistoryChart.kt index 699cc2ee6..f0da7a196 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/views/HistoryChart.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/views/HistoryChart.kt @@ -41,6 +41,7 @@ class HistoryChart( var firstWeekday: DayOfWeek, var paletteColor: PaletteColor, var series: List, + var defaultSquare: Square, var theme: Theme, var today: LocalDate, var onDateClickedListener: OnDateClickedListener = OnDateClickedListener { }, @@ -189,7 +190,7 @@ class HistoryChart( offset: Int, ) { - val value = if (offset >= series.size) Square.OFF else series[offset] + val value = if (offset >= series.size) defaultSquare else series[offset] val squareColor: Color val color = theme.color(paletteColor.paletteIndex) squareColor = when (value) { diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/views/HistoryChartTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/views/HistoryChartTest.kt index bce63bcec..a53135f9b 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/views/HistoryChartTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/views/HistoryChartTest.kt @@ -49,6 +49,7 @@ class HistoryChartTest { dateFormatter = JavaLocalDateFormatter(Locale.US), firstWeekday = SUNDAY, onDateClickedListener = dateClickedListener, + defaultSquare = OFF, series = listOf( 2, // today 2, 1, 2, 1, 2, 1, 2,