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 de2401c46..8c62e337a 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
@@ -22,6 +22,7 @@ package org.isoron.uhabits.activities.common.dialogs
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
+import android.content.DialogInterface.BUTTON_NEGATIVE
import android.text.InputFilter
import android.text.Spanned
import android.view.LayoutInflater
@@ -33,7 +34,11 @@ 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.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
@@ -52,6 +57,7 @@ class NumberPickerFactory
unit: String,
notes: String,
dateString: String,
+ frequency: Frequency,
callback: ListHabitsBehavior.NumberPickerCallback
): AlertDialog {
val inflater = LayoutInflater.from(context)
@@ -92,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,9 +114,24 @@ class NumberPickerFactory
.setOnDismissListener {
callback.onNumberPickerDismissed()
}
- .create()
+
+ 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)
+ }
+ }
+
+ val dialog = dialogBuilder.create()
dialog.setOnShowListener {
+ val preferences =
+ (context.applicationContext as HabitsApplication).component.preferences
+ if (!preferences.isSkipEnabled) {
+ dialog.getButton(BUTTON_NEGATIVE).visibility = View.GONE
+ }
showSoftInput(dialog, pickerInputText)
}
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..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
@@ -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,14 @@ 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
// 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-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/list/views/NumberButtonView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt
index d0d705fb3..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
@@ -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.models.NumericalHabitType.AT_LEAST
import org.isoron.uhabits.core.models.NumericalHabitType.AT_MOST
import org.isoron.uhabits.core.preferences.Preferences
@@ -143,6 +144,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
@@ -176,6 +183,11 @@ class NumberButtonView(
val textSize: Float
when {
+ 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
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-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.
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..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,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 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)
}
@@ -301,6 +306,31 @@ fun List.groupedSum(
}.entries.map { (timestamp, entries) ->
Entry(timestamp, entries.sumOf { it.value })
}.sortedBy { (timestamp, _) ->
- - timestamp.unixTime
+ -timestamp.unixTime
+ }
+}
+
+/**
+ * Counts the number of days with vaLue SKIP in the given period.
+ */
+fun List.countSkippedDays(
+ truncateField: DateUtils.TruncateField,
+ firstWeekday: Int = Calendar.SATURDAY
+): 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/models/ScoreList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/ScoreList.kt
index 23bd88fd1..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,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
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..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,6 +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.Frequency
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.HabitType
@@ -58,6 +59,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 +169,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 de1820bbd..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
@@ -27,6 +27,7 @@ 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
@@ -123,6 +124,7 @@ class HistoryCardPresenter(
habit.unit,
entry.notes,
timestamp.toDialogDateString(),
+ frequency = habit.frequency
) { newValue: Double, newNotes: String ->
val thousands = (newValue * 1000).roundToInt()
commandRunner.run(
@@ -154,6 +156,7 @@ class HistoryCardPresenter(
entries.map {
when {
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
@@ -196,8 +199,10 @@ class HistoryCardPresenter(
unit: String,
notes: String,
dateString: String,
- callback: ListHabitsBehavior.NumberPickerCallback,
+ frequency: Frequency,
+ callback: ListHabitsBehavior.NumberPickerCallback
)
+
fun showCheckmarkDialog(
selectedValue: Int,
notes: String,
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..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
@@ -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,59 @@ class TargetCardPresenter {
isNumerical = habit.isNumerical
).firstOrNull()?.value ?: 0
+ val skippedDayToday = entries.countSkippedDays(
+ truncateField = DateUtils.TruncateField.DAY
+ ).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
+ ).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,
+ ).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
+ ).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
+ ).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)
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)
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..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"), 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))