Added a notes field and implemented dialog for numeric habits

This commit is contained in:
Bindu
2021-09-13 07:05:17 -07:00
parent c50c5af497
commit d38f83e961
26 changed files with 200 additions and 60 deletions

View File

@@ -20,4 +20,4 @@ package org.isoron.uhabits.core
const val DATABASE_FILENAME = "uhabits.db"
const val DATABASE_VERSION = 24
const val DATABASE_VERSION = 25

View File

@@ -28,10 +28,11 @@ data class CreateRepetitionCommand(
val habit: Habit,
val timestamp: Timestamp,
val value: Int,
val notes: String,
) : Command {
override fun run() {
val entries = habit.originalEntries
entries.add(Entry(timestamp, value))
entries.add(Entry(timestamp, value, notes))
habit.recompute()
habitList.resort()
}

View File

@@ -77,7 +77,8 @@ class HabitBullCSVImporter
logger.info("Creating habit: $name")
}
if (parseInt(cols[4]) == 1) {
h.originalEntries.add(Entry(timestamp, Entry.YES_MANUAL))
val notes = cols[5] ?: ""
h.originalEntries.add(Entry(timestamp, Entry.YES_MANUAL, notes))
}
}
}

View File

@@ -101,8 +101,8 @@ class LoopDBImporter
for (r in entryRecords) {
val t = Timestamp(r.timestamp!!)
val (_, value) = habit!!.originalEntries.get(t)
if (value != r.value) CreateRepetitionCommand(habitList, habit, t, r.value!!).run()
val (_, value, notes) = habit!!.originalEntries.get(t)
if (value != r.value || notes != r.notes) CreateRepetitionCommand(habitList, habit, t, r.value!!, r.notes!!).run()
}
runner.notifyListeners(command)

View File

@@ -21,6 +21,7 @@ package org.isoron.uhabits.core.models
data class Entry(
val timestamp: Timestamp,
val value: Int,
val notes: String = "",
) {
companion object {
/**

View File

@@ -41,12 +41,16 @@ class EntryRecord {
@field:Column
var id: Long? = null
@field:Column
var notes: String? = null
fun copyFrom(entry: Entry) {
timestamp = entry.timestamp.unixTime
value = entry.value
notes = entry.notes
}
fun toEntry(): Entry {
return Entry(Timestamp(timestamp!!), value!!)
return Entry(Timestamp(timestamp!!), value!!, notes!!)
}
}

View File

@@ -78,6 +78,11 @@ class HabitCardListCache @Inject constructor(
return data.checkmarks[habitId]!!
}
@Synchronized
fun getNoteIndicators(habitId: Long): BooleanArray {
return data.notesIndicators[habitId]!!
}
@Synchronized
fun hasNoHabit(): Boolean {
return allHabits.isEmpty
@@ -163,6 +168,7 @@ class HabitCardListCache @Inject constructor(
data.habits.removeAt(position)
data.idToHabit.remove(id)
data.checkmarks.remove(id)
data.notesIndicators.remove(id)
data.scores.remove(id)
listener.onItemRemoved(position)
}
@@ -207,6 +213,7 @@ class HabitCardListCache @Inject constructor(
val habits: MutableList<Habit>
val checkmarks: HashMap<Long?, IntArray>
val scores: HashMap<Long?, Double>
val notesIndicators: HashMap<Long?, BooleanArray>
@Synchronized
fun copyCheckmarksFrom(oldData: CacheData) {
@@ -217,6 +224,15 @@ class HabitCardListCache @Inject constructor(
}
}
@Synchronized
fun copyNoteIndicatorsFrom(oldData: CacheData) {
val empty = BooleanArray(checkmarkCount)
for (id in idToHabit.keys) {
if (oldData.notesIndicators.containsKey(id)) notesIndicators[id] =
oldData.notesIndicators[id]!! else notesIndicators[id] = empty
}
}
@Synchronized
fun copyScoresFrom(oldData: CacheData) {
for (id in idToHabit.keys) {
@@ -241,6 +257,7 @@ class HabitCardListCache @Inject constructor(
habits = LinkedList()
checkmarks = HashMap()
scores = HashMap()
notesIndicators = HashMap()
}
}
@@ -271,6 +288,7 @@ class HabitCardListCache @Inject constructor(
newData.fetchHabits()
newData.copyScoresFrom(data)
newData.copyCheckmarksFrom(data)
newData.copyNoteIndicatorsFrom(data)
val today = getTodayWithOffset()
val dateFrom = today.minus(checkmarkCount - 1)
if (runner != null) runner!!.publishProgress(this, -1)
@@ -280,10 +298,14 @@ class HabitCardListCache @Inject constructor(
if (targetId != null && targetId != habit.id) continue
newData.scores[habit.id] = habit.scores[today].value
val list: MutableList<Int> = ArrayList()
for ((_, value) in habit.computedEntries.getByInterval(dateFrom, today))
val notesList: MutableList<Boolean> = ArrayList()
for ((_, value, note) in habit.computedEntries.getByInterval(dateFrom, today)) {
list.add(value)
if (note.isNotEmpty()) notesList.add(true) else notesList.add(false)
}
val entries = list.toTypedArray()
newData.checkmarks[habit.id] = ArrayUtils.toPrimitive(entries)
newData.notesIndicators[habit.id] = notesList.toBooleanArray()
runner!!.publishProgress(this, position)
}
}
@@ -311,6 +333,7 @@ class HabitCardListCache @Inject constructor(
data.idToHabit[id] = habit
data.scores[id] = newData.scores[id]!!
data.checkmarks[id] = newData.checkmarks[id]!!
data.notesIndicators[id] = newData.notesIndicators[id]!!
listener.onItemInserted(position)
}
@@ -338,14 +361,18 @@ class HabitCardListCache @Inject constructor(
private fun performUpdate(id: Long, position: Int) {
val oldScore = data.scores[id]!!
val oldCheckmarks = data.checkmarks[id]
val oldNoteIndicators = data.notesIndicators[id]
val newScore = newData.scores[id]!!
val newCheckmarks = newData.checkmarks[id]!!
val newNoteIndicators = newData.notesIndicators[id]!!
var unchanged = true
if (oldScore != newScore) unchanged = false
if (!Arrays.equals(oldCheckmarks, newCheckmarks)) unchanged = false
if (!Arrays.equals(oldNoteIndicators, newNoteIndicators)) unchanged = false
if (unchanged) return
data.scores[id] = newScore
data.checkmarks[id] = newCheckmarks
data.notesIndicators[id] = newNoteIndicators
listener.onItemChanged(position)
}

View File

@@ -47,14 +47,16 @@ open class ListHabitsBehavior @Inject constructor(
}
fun onEdit(habit: Habit, timestamp: Timestamp?) {
val entries = habit.computedEntries
val oldValue = entries.get(timestamp!!).value.toDouble()
val entries = habit.computedEntries.get(timestamp!!)
val oldValue = entries.value.toDouble()
val notes = entries.notes
screen.showNumberPicker(
oldValue / 1000,
habit.unit
) { newValue: Double ->
habit.unit,
notes
) { newValue: Double, newNotes:String, ->
val value = (newValue * 1000).roundToInt()
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, value))
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, value, newNotes))
}
}
@@ -104,9 +106,9 @@ open class ListHabitsBehavior @Inject constructor(
if (prefs.isFirstRun) onFirstRun()
}
fun onToggle(habit: Habit, timestamp: Timestamp?, value: Int) {
fun onToggle(habit: Habit, timestamp: Timestamp?, value: Int, notes: String) {
commandRunner.run(
CreateRepetitionCommand(habitList, habit, timestamp!!, value)
CreateRepetitionCommand(habitList, habit, timestamp!!, value, notes)
)
}
@@ -131,7 +133,7 @@ open class ListHabitsBehavior @Inject constructor(
}
fun interface NumberPickerCallback {
fun onNumberPicked(newValue: Double)
fun onNumberPicked(newValue: Double, notes: String)
fun onNumberPickerDismissed() {}
}
@@ -142,6 +144,7 @@ open class ListHabitsBehavior @Inject constructor(
fun showNumberPicker(
value: Double,
unit: String,
notes: String,
callback: NumberPickerCallback
)

View File

@@ -64,7 +64,8 @@ class HistoryCardPresenter(
if (habit.isNumerical) {
val entries = habit.computedEntries
val oldValue = entries.get(timestamp).value
screen.showNumberPicker(oldValue / 1000.0, habit.unit) { newValue: Double ->
val notes = entries.get(timestamp).notes
screen.showNumberPicker(oldValue / 1000.0, habit.unit, notes) { newValue: Double, newNotes: String ->
val thousands = (newValue * 1000).roundToInt()
commandRunner.run(
CreateRepetitionCommand(
@@ -72,11 +73,14 @@ class HistoryCardPresenter(
habit,
timestamp,
thousands,
newNotes,
),
)
}
} else {
val currentValue = habit.computedEntries.get(timestamp).value
val entry = habit.computedEntries.get(timestamp)
val currentValue = entry.value
val notes = entry.notes
val nextValue = Entry.nextToggleValue(
value = currentValue,
isSkipEnabled = preferences.isSkipEnabled,
@@ -88,6 +92,7 @@ class HistoryCardPresenter(
habit,
timestamp,
nextValue,
notes,
),
)
}
@@ -154,6 +159,7 @@ class HistoryCardPresenter(
fun showNumberPicker(
value: Double,
unit: String,
notes: String,
callback: ListHabitsBehavior.NumberPickerCallback,
)
}

View File

@@ -46,31 +46,37 @@ class WidgetBehavior @Inject constructor(
}
fun onToggleRepetition(habit: Habit, timestamp: Timestamp) {
val currentValue = habit.originalEntries.get(timestamp).value
val entry = habit.computedEntries.get(timestamp)
val currentValue = entry.value
val notes = entry.notes
val newValue = nextToggleValue(
value = currentValue,
isSkipEnabled = preferences.isSkipEnabled,
areQuestionMarksEnabled = preferences.areQuestionMarksEnabled
)
setValue(habit, timestamp, newValue)
setValue(habit, timestamp, newValue, notes)
notificationTray.cancel(habit)
}
fun onIncrement(habit: Habit, timestamp: Timestamp, amount: Int) {
val currentValue = habit.computedEntries.get(timestamp).value
setValue(habit, timestamp, currentValue + amount)
val entry = habit.computedEntries.get(timestamp)
val currentValue = entry.value
val notes = entry.notes
setValue(habit, timestamp, currentValue + amount, notes)
notificationTray.cancel(habit)
}
fun onDecrement(habit: Habit, timestamp: Timestamp, amount: Int) {
val currentValue = habit.computedEntries.get(timestamp).value
setValue(habit, timestamp, currentValue - amount)
val entry = habit.computedEntries.get(timestamp)
val currentValue = entry.value
val notes = entry.notes
setValue(habit, timestamp, currentValue - amount, notes)
notificationTray.cancel(habit)
}
fun setValue(habit: Habit, timestamp: Timestamp?, newValue: Int) {
fun setValue(habit: Habit, timestamp: Timestamp?, newValue: Int, notes: String = "") {
commandRunner.run(
CreateRepetitionCommand(habitList, habit, timestamp!!, newValue)
CreateRepetitionCommand(habitList, habit, timestamp!!, newValue, notes)
)
}
}

View File

@@ -0,0 +1 @@
alter table Repetitions add column notes text;

View File

@@ -38,7 +38,7 @@ class CreateRepetitionCommandTest : BaseUnitTest() {
habit = fixtures.createShortHabit()
habitList.add(habit)
today = getToday()
command = CreateRepetitionCommand(habitList, habit, today, 100)
command = CreateRepetitionCommand(habitList, habit, today, 100, "")
}
@Test

View File

@@ -79,8 +79,8 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
@Test
fun testOnEdit() {
behavior.onEdit(habit2, getToday())
verify(screen).showNumberPicker(eq(0.1), eq("miles"), picker.capture())
picker.lastValue.onNumberPicked(100.0)
verify(screen).showNumberPicker(eq(0.1), eq("miles"), "", picker.capture())
picker.lastValue.onNumberPicked(100.0, "")
val today = getTodayWithOffset()
assertThat(habit2.computedEntries.get(today).value, equalTo(100000))
}
@@ -160,7 +160,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
@Test
fun testOnToggle() {
assertTrue(habit1.isCompletedToday())
behavior.onToggle(habit1, getToday(), Entry.NO)
behavior.onToggle(habit1, getToday(), Entry.NO, "")
assertFalse(habit1.isCompletedToday())
}
}