mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-07 01:28:52 -06:00
Added a notes field and implemented dialog for numeric habits
This commit is contained in:
@@ -47,6 +47,7 @@ class NumberPickerFactory
|
|||||||
fun create(
|
fun create(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
unit: String,
|
||||||
|
notes: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback
|
callback: ListHabitsBehavior.NumberPickerCallback
|
||||||
): AlertDialog {
|
): AlertDialog {
|
||||||
val inflater = LayoutInflater.from(context)
|
val inflater = LayoutInflater.from(context)
|
||||||
@@ -54,6 +55,7 @@ class NumberPickerFactory
|
|||||||
|
|
||||||
val picker = view.findViewById<NumberPicker>(R.id.picker)
|
val picker = view.findViewById<NumberPicker>(R.id.picker)
|
||||||
val picker2 = view.findViewById<NumberPicker>(R.id.picker2)
|
val picker2 = view.findViewById<NumberPicker>(R.id.picker2)
|
||||||
|
val etNotes = view.findViewById<EditText>(R.id.etNotes)
|
||||||
|
|
||||||
val watcherFilter: InputFilter = SeparatorWatcherInputFilter(picker2)
|
val watcherFilter: InputFilter = SeparatorWatcherInputFilter(picker2)
|
||||||
val numberPickerInputText = getNumberPickerInputText(picker)
|
val numberPickerInputText = getNumberPickerInputText(picker)
|
||||||
@@ -77,13 +79,18 @@ class NumberPickerFactory
|
|||||||
picker2.setFormatter { v -> String.format("%02d", v) }
|
picker2.setFormatter { v -> String.format("%02d", v) }
|
||||||
picker2.value = intValue % 100
|
picker2.value = intValue % 100
|
||||||
|
|
||||||
|
etNotes.setText(notes)
|
||||||
val dialog = AlertDialog.Builder(context)
|
val dialog = AlertDialog.Builder(context)
|
||||||
.setView(view)
|
.setView(view)
|
||||||
.setTitle(R.string.change_value)
|
.setTitle(R.string.change_value)
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.save) { _, _ ->
|
||||||
picker.clearFocus()
|
picker.clearFocus()
|
||||||
val v = picker.value + 0.01 * picker2.value
|
val v = picker.value + 0.01 * picker2.value
|
||||||
callback.onNumberPicked(v)
|
val note = etNotes.text.toString()
|
||||||
|
callback.onNumberPicked(v, note)
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||||
|
callback.onNumberPickerDismissed()
|
||||||
}
|
}
|
||||||
.setOnDismissListener {
|
.setOnDismissListener {
|
||||||
callback.onNumberPickerDismissed()
|
callback.onNumberPickerDismissed()
|
||||||
|
|||||||
@@ -225,9 +225,10 @@ class ListHabitsScreen
|
|||||||
override fun showNumberPicker(
|
override fun showNumberPicker(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
unit: String,
|
||||||
|
notes: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback
|
callback: ListHabitsBehavior.NumberPickerCallback
|
||||||
) {
|
) {
|
||||||
numberPickerFactory.create(value, unit, callback).show()
|
numberPickerFactory.create(value, unit, notes, callback).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExecuteString(command: Command): String? {
|
private fun getExecuteString(command: Command): String? {
|
||||||
|
|||||||
@@ -71,6 +71,12 @@ class CheckmarkButtonView(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasNotes = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
var onToggle: (Int) -> Unit = {}
|
var onToggle: (Int) -> Unit = {}
|
||||||
private var drawer = Drawer()
|
private var drawer = Drawer()
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ class CheckmarkPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var notes = BooleanArray(0)
|
||||||
|
set(values) {
|
||||||
|
field = values
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
var onToggle: (Timestamp, Int) -> Unit = { _, _ -> }
|
var onToggle: (Timestamp, Int) -> Unit = { _, _ -> }
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
@@ -72,6 +78,10 @@ class CheckmarkPanelView(
|
|||||||
index + dataOffset < values.size -> values[index + dataOffset]
|
index + dataOffset < values.size -> values[index + dataOffset]
|
||||||
else -> UNKNOWN
|
else -> UNKNOWN
|
||||||
}
|
}
|
||||||
|
button.hasNotes = when {
|
||||||
|
index + dataOffset < notes.size -> notes[index + dataOffset]
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
button.color = color
|
button.color = color
|
||||||
button.onToggle = { value -> onToggle(timestamp, value) }
|
button.onToggle = { value -> onToggle(timestamp, value) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,8 +124,9 @@ class HabitCardListAdapter @Inject constructor(
|
|||||||
val habit = cache.getHabitByPosition(position)
|
val habit = cache.getHabitByPosition(position)
|
||||||
val score = cache.getScore(habit!!.id!!)
|
val score = cache.getScore(habit!!.id!!)
|
||||||
val checkmarks = cache.getCheckmarks(habit.id!!)
|
val checkmarks = cache.getCheckmarks(habit.id!!)
|
||||||
|
val notesIndicators = cache.getNoteIndicators(habit.id!!)
|
||||||
val selected = selected.contains(habit)
|
val selected = selected.contains(habit)
|
||||||
listView!!.bindCardView(holder, habit, score, checkmarks, selected)
|
listView!!.bindCardView(holder, habit, score, checkmarks, notesIndicators, selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewAttachedToWindow(holder: HabitCardViewHolder) {
|
override fun onViewAttachedToWindow(holder: HabitCardViewHolder) {
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class HabitCardListView(
|
|||||||
habit: Habit,
|
habit: Habit,
|
||||||
score: Double,
|
score: Double,
|
||||||
checkmarks: IntArray,
|
checkmarks: IntArray,
|
||||||
|
notesIndicators: BooleanArray,
|
||||||
selected: Boolean
|
selected: Boolean
|
||||||
): View {
|
): View {
|
||||||
val cardView = holder.itemView as HabitCardView
|
val cardView = holder.itemView as HabitCardView
|
||||||
@@ -98,6 +99,7 @@ class HabitCardListView(
|
|||||||
cardView.score = score
|
cardView.score = score
|
||||||
cardView.unit = habit.unit
|
cardView.unit = habit.unit
|
||||||
cardView.threshold = habit.targetValue / habit.frequency.denominator
|
cardView.threshold = habit.targetValue / habit.frequency.denominator
|
||||||
|
cardView.notes = notesIndicators
|
||||||
|
|
||||||
val detector = GestureDetector(context, CardViewGestureDetector(holder))
|
val detector = GestureDetector(context, CardViewGestureDetector(holder))
|
||||||
cardView.setOnTouchListener { _, ev ->
|
cardView.setOnTouchListener { _, ev ->
|
||||||
|
|||||||
@@ -115,6 +115,13 @@ class HabitCardView(
|
|||||||
numberPanel.threshold = value
|
numberPanel.threshold = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var notes
|
||||||
|
get() = numberPanel.notes
|
||||||
|
set(values) {
|
||||||
|
checkmarkPanel.notes = values
|
||||||
|
numberPanel.notes = values
|
||||||
|
}
|
||||||
|
|
||||||
var checkmarkPanel: CheckmarkPanelView
|
var checkmarkPanel: CheckmarkPanelView
|
||||||
private var numberPanel: NumberPanelView
|
private var numberPanel: NumberPanelView
|
||||||
private var innerFrame: LinearLayout
|
private var innerFrame: LinearLayout
|
||||||
@@ -143,7 +150,7 @@ class HabitCardView(
|
|||||||
checkmarkPanel = checkmarkPanelFactory.create().apply {
|
checkmarkPanel = checkmarkPanelFactory.create().apply {
|
||||||
onToggle = { timestamp, value ->
|
onToggle = { timestamp, value ->
|
||||||
triggerRipple(timestamp)
|
triggerRipple(timestamp)
|
||||||
habit?.let { behavior.onToggle(it, timestamp, value) }
|
habit?.let { behavior.onToggle(it, timestamp, value, "") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ class NumberButtonView(
|
|||||||
field = value
|
field = value
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
var hasNotes = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
var onEdit: () -> Unit = {}
|
var onEdit: () -> Unit = {}
|
||||||
private var drawer: Drawer = Drawer(context)
|
private var drawer: Drawer = Drawer(context)
|
||||||
@@ -111,8 +116,7 @@ class NumberButtonView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
if (preferences.isShortToggleEnabled) onEdit()
|
onEdit()
|
||||||
else showMessage(resources.getString(R.string.long_press_to_edit))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View): Boolean {
|
override fun onLongClick(v: View): Boolean {
|
||||||
@@ -153,6 +157,8 @@ class NumberButtonView(
|
|||||||
textAlign = Paint.Align.CENTER
|
textAlign = Paint.Align.CENTER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val pNotesIndicator: Paint = Paint()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
em = pNumber.measureText("m")
|
em = pNumber.measureText("m")
|
||||||
lowContrast = sres.getColor(R.attr.contrast40)
|
lowContrast = sres.getColor(R.attr.contrast40)
|
||||||
@@ -200,6 +206,7 @@ class NumberButtonView(
|
|||||||
pNumber.color = activeColor
|
pNumber.color = activeColor
|
||||||
pNumber.typeface = typeface
|
pNumber.typeface = typeface
|
||||||
pUnit.color = activeColor
|
pUnit.color = activeColor
|
||||||
|
pNotesIndicator.color = activeColor
|
||||||
|
|
||||||
if (units.isBlank()) {
|
if (units.isBlank()) {
|
||||||
rect.set(0f, 0f, width.toFloat(), height.toFloat())
|
rect.set(0f, 0f, width.toFloat(), height.toFloat())
|
||||||
@@ -211,6 +218,11 @@ class NumberButtonView(
|
|||||||
rect.offset(0f, 1.3f * em)
|
rect.offset(0f, 1.3f * em)
|
||||||
canvas.drawText(units, rect.centerX(), rect.centerY(), pUnit)
|
canvas.drawText(units, rect.centerX(), rect.centerY(), pUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasNotes) {
|
||||||
|
val cy = 0.8f * em
|
||||||
|
canvas.drawCircle(width.toFloat() - cy, cy, 8f, pNotesIndicator)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ class NumberPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var notes = BooleanArray(0)
|
||||||
|
set(values) {
|
||||||
|
field = values
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
var onEdit: (Timestamp) -> Unit = {}
|
var onEdit: (Timestamp) -> Unit = {}
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
@@ -90,6 +96,10 @@ class NumberPanelView(
|
|||||||
index + dataOffset < values.size -> values[index + dataOffset]
|
index + dataOffset < values.size -> values[index + dataOffset]
|
||||||
else -> 0.0
|
else -> 0.0
|
||||||
}
|
}
|
||||||
|
button.hasNotes = when {
|
||||||
|
index + dataOffset < notes.size -> notes[index + dataOffset]
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
button.color = color
|
button.color = color
|
||||||
button.targetType = targetType
|
button.targetType = targetType
|
||||||
button.threshold = threshold
|
button.threshold = threshold
|
||||||
|
|||||||
@@ -164,9 +164,10 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
|||||||
override fun showNumberPicker(
|
override fun showNumberPicker(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
unit: String,
|
||||||
|
notes: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback,
|
callback: ListHabitsBehavior.NumberPickerCallback,
|
||||||
) {
|
) {
|
||||||
NumberPickerFactory(this@ShowHabitActivity).create(value, unit, callback).show()
|
NumberPickerFactory(this@ShowHabitActivity).create(value, unit, notes, callback).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showEditHabitScreen(habit: Habit) {
|
override fun showEditHabitScreen(habit: Habit) {
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ class NumericalCheckmarkWidgetActivity : Activity(), ListHabitsBehavior.NumberPi
|
|||||||
SystemUtils.unlockScreen(this)
|
SystemUtils.unlockScreen(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNumberPicked(newValue: Double) {
|
override fun onNumberPicked(newValue: Double, notes: String) {
|
||||||
behavior.setValue(data.habit, data.timestamp, (newValue * 1000).toInt())
|
behavior.setValue(data.habit, data.timestamp, (newValue * 1000).toInt(), notes)
|
||||||
widgetUpdater.updateWidgets()
|
widgetUpdater.updateWidgets()
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@@ -79,6 +79,7 @@ class NumericalCheckmarkWidgetActivity : Activity(), ListHabitsBehavior.NumberPi
|
|||||||
numberPickerFactory.create(
|
numberPickerFactory.create(
|
||||||
entry.value / 1000.0,
|
entry.value / 1000.0,
|
||||||
data.habit.unit,
|
data.habit.unit,
|
||||||
|
entry.notes,
|
||||||
this
|
this
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,16 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="horizontal"
|
android:orientation="vertical"
|
||||||
android:gravity="center"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<NumberPicker
|
<NumberPicker
|
||||||
android:id="@+id/picker"
|
android:id="@+id/picker"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@@ -49,3 +54,29 @@
|
|||||||
android:layout_height="wrap_content"/>
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="30dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/TextAppearance.AppCompat.Title"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:text="@string/notes"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etNotes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textCapSentences|textMultiLine"
|
||||||
|
style="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:hint="@string/example_notes"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -55,6 +55,7 @@
|
|||||||
<string name="clear">Clear</string>
|
<string name="clear">Clear</string>
|
||||||
<string name="reminder">Reminder</string>
|
<string name="reminder">Reminder</string>
|
||||||
<string name="save">Save</string>
|
<string name="save">Save</string>
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
<string name="streaks">Streaks</string>
|
<string name="streaks">Streaks</string>
|
||||||
<string name="no_habits_found">You have no active habits</string>
|
<string name="no_habits_found">You have no active habits</string>
|
||||||
<string name="no_habits_left_to_do">You\'re all done for today!</string>
|
<string name="no_habits_left_to_do">You\'re all done for today!</string>
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ package org.isoron.uhabits.core
|
|||||||
|
|
||||||
const val DATABASE_FILENAME = "uhabits.db"
|
const val DATABASE_FILENAME = "uhabits.db"
|
||||||
|
|
||||||
const val DATABASE_VERSION = 24
|
const val DATABASE_VERSION = 25
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ data class CreateRepetitionCommand(
|
|||||||
val habit: Habit,
|
val habit: Habit,
|
||||||
val timestamp: Timestamp,
|
val timestamp: Timestamp,
|
||||||
val value: Int,
|
val value: Int,
|
||||||
|
val notes: String,
|
||||||
) : Command {
|
) : Command {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val entries = habit.originalEntries
|
val entries = habit.originalEntries
|
||||||
entries.add(Entry(timestamp, value))
|
entries.add(Entry(timestamp, value, notes))
|
||||||
habit.recompute()
|
habit.recompute()
|
||||||
habitList.resort()
|
habitList.resort()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ class HabitBullCSVImporter
|
|||||||
logger.info("Creating habit: $name")
|
logger.info("Creating habit: $name")
|
||||||
}
|
}
|
||||||
if (parseInt(cols[4]) == 1) {
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,8 +101,8 @@ class LoopDBImporter
|
|||||||
|
|
||||||
for (r in entryRecords) {
|
for (r in entryRecords) {
|
||||||
val t = Timestamp(r.timestamp!!)
|
val t = Timestamp(r.timestamp!!)
|
||||||
val (_, value) = habit!!.originalEntries.get(t)
|
val (_, value, notes) = habit!!.originalEntries.get(t)
|
||||||
if (value != r.value) CreateRepetitionCommand(habitList, habit, t, r.value!!).run()
|
if (value != r.value || notes != r.notes) CreateRepetitionCommand(habitList, habit, t, r.value!!, r.notes!!).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
runner.notifyListeners(command)
|
runner.notifyListeners(command)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package org.isoron.uhabits.core.models
|
|||||||
data class Entry(
|
data class Entry(
|
||||||
val timestamp: Timestamp,
|
val timestamp: Timestamp,
|
||||||
val value: Int,
|
val value: Int,
|
||||||
|
val notes: String = "",
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,12 +41,16 @@ class EntryRecord {
|
|||||||
|
|
||||||
@field:Column
|
@field:Column
|
||||||
var id: Long? = null
|
var id: Long? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var notes: String? = null
|
||||||
fun copyFrom(entry: Entry) {
|
fun copyFrom(entry: Entry) {
|
||||||
timestamp = entry.timestamp.unixTime
|
timestamp = entry.timestamp.unixTime
|
||||||
value = entry.value
|
value = entry.value
|
||||||
|
notes = entry.notes
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toEntry(): Entry {
|
fun toEntry(): Entry {
|
||||||
return Entry(Timestamp(timestamp!!), value!!)
|
return Entry(Timestamp(timestamp!!), value!!, notes!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,11 @@ class HabitCardListCache @Inject constructor(
|
|||||||
return data.checkmarks[habitId]!!
|
return data.checkmarks[habitId]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun getNoteIndicators(habitId: Long): BooleanArray {
|
||||||
|
return data.notesIndicators[habitId]!!
|
||||||
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun hasNoHabit(): Boolean {
|
fun hasNoHabit(): Boolean {
|
||||||
return allHabits.isEmpty
|
return allHabits.isEmpty
|
||||||
@@ -163,6 +168,7 @@ class HabitCardListCache @Inject constructor(
|
|||||||
data.habits.removeAt(position)
|
data.habits.removeAt(position)
|
||||||
data.idToHabit.remove(id)
|
data.idToHabit.remove(id)
|
||||||
data.checkmarks.remove(id)
|
data.checkmarks.remove(id)
|
||||||
|
data.notesIndicators.remove(id)
|
||||||
data.scores.remove(id)
|
data.scores.remove(id)
|
||||||
listener.onItemRemoved(position)
|
listener.onItemRemoved(position)
|
||||||
}
|
}
|
||||||
@@ -207,6 +213,7 @@ class HabitCardListCache @Inject constructor(
|
|||||||
val habits: MutableList<Habit>
|
val habits: MutableList<Habit>
|
||||||
val checkmarks: HashMap<Long?, IntArray>
|
val checkmarks: HashMap<Long?, IntArray>
|
||||||
val scores: HashMap<Long?, Double>
|
val scores: HashMap<Long?, Double>
|
||||||
|
val notesIndicators: HashMap<Long?, BooleanArray>
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun copyCheckmarksFrom(oldData: CacheData) {
|
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
|
@Synchronized
|
||||||
fun copyScoresFrom(oldData: CacheData) {
|
fun copyScoresFrom(oldData: CacheData) {
|
||||||
for (id in idToHabit.keys) {
|
for (id in idToHabit.keys) {
|
||||||
@@ -241,6 +257,7 @@ class HabitCardListCache @Inject constructor(
|
|||||||
habits = LinkedList()
|
habits = LinkedList()
|
||||||
checkmarks = HashMap()
|
checkmarks = HashMap()
|
||||||
scores = HashMap()
|
scores = HashMap()
|
||||||
|
notesIndicators = HashMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,6 +288,7 @@ class HabitCardListCache @Inject constructor(
|
|||||||
newData.fetchHabits()
|
newData.fetchHabits()
|
||||||
newData.copyScoresFrom(data)
|
newData.copyScoresFrom(data)
|
||||||
newData.copyCheckmarksFrom(data)
|
newData.copyCheckmarksFrom(data)
|
||||||
|
newData.copyNoteIndicatorsFrom(data)
|
||||||
val today = getTodayWithOffset()
|
val today = getTodayWithOffset()
|
||||||
val dateFrom = today.minus(checkmarkCount - 1)
|
val dateFrom = today.minus(checkmarkCount - 1)
|
||||||
if (runner != null) runner!!.publishProgress(this, -1)
|
if (runner != null) runner!!.publishProgress(this, -1)
|
||||||
@@ -280,10 +298,14 @@ class HabitCardListCache @Inject constructor(
|
|||||||
if (targetId != null && targetId != habit.id) continue
|
if (targetId != null && targetId != habit.id) continue
|
||||||
newData.scores[habit.id] = habit.scores[today].value
|
newData.scores[habit.id] = habit.scores[today].value
|
||||||
val list: MutableList<Int> = ArrayList()
|
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)
|
list.add(value)
|
||||||
|
if (note.isNotEmpty()) notesList.add(true) else notesList.add(false)
|
||||||
|
}
|
||||||
val entries = list.toTypedArray()
|
val entries = list.toTypedArray()
|
||||||
newData.checkmarks[habit.id] = ArrayUtils.toPrimitive(entries)
|
newData.checkmarks[habit.id] = ArrayUtils.toPrimitive(entries)
|
||||||
|
newData.notesIndicators[habit.id] = notesList.toBooleanArray()
|
||||||
runner!!.publishProgress(this, position)
|
runner!!.publishProgress(this, position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,6 +333,7 @@ class HabitCardListCache @Inject constructor(
|
|||||||
data.idToHabit[id] = habit
|
data.idToHabit[id] = habit
|
||||||
data.scores[id] = newData.scores[id]!!
|
data.scores[id] = newData.scores[id]!!
|
||||||
data.checkmarks[id] = newData.checkmarks[id]!!
|
data.checkmarks[id] = newData.checkmarks[id]!!
|
||||||
|
data.notesIndicators[id] = newData.notesIndicators[id]!!
|
||||||
listener.onItemInserted(position)
|
listener.onItemInserted(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,14 +361,18 @@ class HabitCardListCache @Inject constructor(
|
|||||||
private fun performUpdate(id: Long, position: Int) {
|
private fun performUpdate(id: Long, position: Int) {
|
||||||
val oldScore = data.scores[id]!!
|
val oldScore = data.scores[id]!!
|
||||||
val oldCheckmarks = data.checkmarks[id]
|
val oldCheckmarks = data.checkmarks[id]
|
||||||
|
val oldNoteIndicators = data.notesIndicators[id]
|
||||||
val newScore = newData.scores[id]!!
|
val newScore = newData.scores[id]!!
|
||||||
val newCheckmarks = newData.checkmarks[id]!!
|
val newCheckmarks = newData.checkmarks[id]!!
|
||||||
|
val newNoteIndicators = newData.notesIndicators[id]!!
|
||||||
var unchanged = true
|
var unchanged = true
|
||||||
if (oldScore != newScore) unchanged = false
|
if (oldScore != newScore) unchanged = false
|
||||||
if (!Arrays.equals(oldCheckmarks, newCheckmarks)) unchanged = false
|
if (!Arrays.equals(oldCheckmarks, newCheckmarks)) unchanged = false
|
||||||
|
if (!Arrays.equals(oldNoteIndicators, newNoteIndicators)) unchanged = false
|
||||||
if (unchanged) return
|
if (unchanged) return
|
||||||
data.scores[id] = newScore
|
data.scores[id] = newScore
|
||||||
data.checkmarks[id] = newCheckmarks
|
data.checkmarks[id] = newCheckmarks
|
||||||
|
data.notesIndicators[id] = newNoteIndicators
|
||||||
listener.onItemChanged(position)
|
listener.onItemChanged(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,14 +47,16 @@ open class ListHabitsBehavior @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onEdit(habit: Habit, timestamp: Timestamp?) {
|
fun onEdit(habit: Habit, timestamp: Timestamp?) {
|
||||||
val entries = habit.computedEntries
|
val entries = habit.computedEntries.get(timestamp!!)
|
||||||
val oldValue = entries.get(timestamp!!).value.toDouble()
|
val oldValue = entries.value.toDouble()
|
||||||
|
val notes = entries.notes
|
||||||
screen.showNumberPicker(
|
screen.showNumberPicker(
|
||||||
oldValue / 1000,
|
oldValue / 1000,
|
||||||
habit.unit
|
habit.unit,
|
||||||
) { newValue: Double ->
|
notes
|
||||||
|
) { newValue: Double, newNotes:String, ->
|
||||||
val value = (newValue * 1000).roundToInt()
|
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()
|
if (prefs.isFirstRun) onFirstRun()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onToggle(habit: Habit, timestamp: Timestamp?, value: Int) {
|
fun onToggle(habit: Habit, timestamp: Timestamp?, value: Int, notes: String) {
|
||||||
commandRunner.run(
|
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 interface NumberPickerCallback {
|
||||||
fun onNumberPicked(newValue: Double)
|
fun onNumberPicked(newValue: Double, notes: String)
|
||||||
fun onNumberPickerDismissed() {}
|
fun onNumberPickerDismissed() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +144,7 @@ open class ListHabitsBehavior @Inject constructor(
|
|||||||
fun showNumberPicker(
|
fun showNumberPicker(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
unit: String,
|
||||||
|
notes: String,
|
||||||
callback: NumberPickerCallback
|
callback: NumberPickerCallback
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ class HistoryCardPresenter(
|
|||||||
if (habit.isNumerical) {
|
if (habit.isNumerical) {
|
||||||
val entries = habit.computedEntries
|
val entries = habit.computedEntries
|
||||||
val oldValue = entries.get(timestamp).value
|
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()
|
val thousands = (newValue * 1000).roundToInt()
|
||||||
commandRunner.run(
|
commandRunner.run(
|
||||||
CreateRepetitionCommand(
|
CreateRepetitionCommand(
|
||||||
@@ -72,11 +73,14 @@ class HistoryCardPresenter(
|
|||||||
habit,
|
habit,
|
||||||
timestamp,
|
timestamp,
|
||||||
thousands,
|
thousands,
|
||||||
|
newNotes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} 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(
|
val nextValue = Entry.nextToggleValue(
|
||||||
value = currentValue,
|
value = currentValue,
|
||||||
isSkipEnabled = preferences.isSkipEnabled,
|
isSkipEnabled = preferences.isSkipEnabled,
|
||||||
@@ -88,6 +92,7 @@ class HistoryCardPresenter(
|
|||||||
habit,
|
habit,
|
||||||
timestamp,
|
timestamp,
|
||||||
nextValue,
|
nextValue,
|
||||||
|
notes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -154,6 +159,7 @@ class HistoryCardPresenter(
|
|||||||
fun showNumberPicker(
|
fun showNumberPicker(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
unit: String,
|
||||||
|
notes: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback,
|
callback: ListHabitsBehavior.NumberPickerCallback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,31 +46,37 @@ class WidgetBehavior @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onToggleRepetition(habit: Habit, timestamp: Timestamp) {
|
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(
|
val newValue = nextToggleValue(
|
||||||
value = currentValue,
|
value = currentValue,
|
||||||
isSkipEnabled = preferences.isSkipEnabled,
|
isSkipEnabled = preferences.isSkipEnabled,
|
||||||
areQuestionMarksEnabled = preferences.areQuestionMarksEnabled
|
areQuestionMarksEnabled = preferences.areQuestionMarksEnabled
|
||||||
)
|
)
|
||||||
setValue(habit, timestamp, newValue)
|
setValue(habit, timestamp, newValue, notes)
|
||||||
notificationTray.cancel(habit)
|
notificationTray.cancel(habit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onIncrement(habit: Habit, timestamp: Timestamp, amount: Int) {
|
fun onIncrement(habit: Habit, timestamp: Timestamp, amount: Int) {
|
||||||
val currentValue = habit.computedEntries.get(timestamp).value
|
val entry = habit.computedEntries.get(timestamp)
|
||||||
setValue(habit, timestamp, currentValue + amount)
|
val currentValue = entry.value
|
||||||
|
val notes = entry.notes
|
||||||
|
setValue(habit, timestamp, currentValue + amount, notes)
|
||||||
notificationTray.cancel(habit)
|
notificationTray.cancel(habit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onDecrement(habit: Habit, timestamp: Timestamp, amount: Int) {
|
fun onDecrement(habit: Habit, timestamp: Timestamp, amount: Int) {
|
||||||
val currentValue = habit.computedEntries.get(timestamp).value
|
val entry = habit.computedEntries.get(timestamp)
|
||||||
setValue(habit, timestamp, currentValue - amount)
|
val currentValue = entry.value
|
||||||
|
val notes = entry.notes
|
||||||
|
setValue(habit, timestamp, currentValue - amount, notes)
|
||||||
notificationTray.cancel(habit)
|
notificationTray.cancel(habit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setValue(habit: Habit, timestamp: Timestamp?, newValue: Int) {
|
fun setValue(habit: Habit, timestamp: Timestamp?, newValue: Int, notes: String = "") {
|
||||||
commandRunner.run(
|
commandRunner.run(
|
||||||
CreateRepetitionCommand(habitList, habit, timestamp!!, newValue)
|
CreateRepetitionCommand(habitList, habit, timestamp!!, newValue, notes)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
uhabits-core/src/jvmMain/resources/migrations/25.sql
Normal file
1
uhabits-core/src/jvmMain/resources/migrations/25.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
alter table Repetitions add column notes text;
|
||||||
@@ -38,7 +38,7 @@ class CreateRepetitionCommandTest : BaseUnitTest() {
|
|||||||
habit = fixtures.createShortHabit()
|
habit = fixtures.createShortHabit()
|
||||||
habitList.add(habit)
|
habitList.add(habit)
|
||||||
today = getToday()
|
today = getToday()
|
||||||
command = CreateRepetitionCommand(habitList, habit, today, 100)
|
command = CreateRepetitionCommand(habitList, habit, today, 100, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun testOnEdit() {
|
fun testOnEdit() {
|
||||||
behavior.onEdit(habit2, getToday())
|
behavior.onEdit(habit2, getToday())
|
||||||
verify(screen).showNumberPicker(eq(0.1), eq("miles"), picker.capture())
|
verify(screen).showNumberPicker(eq(0.1), eq("miles"), "", picker.capture())
|
||||||
picker.lastValue.onNumberPicked(100.0)
|
picker.lastValue.onNumberPicked(100.0, "")
|
||||||
val today = getTodayWithOffset()
|
val today = getTodayWithOffset()
|
||||||
assertThat(habit2.computedEntries.get(today).value, equalTo(100000))
|
assertThat(habit2.computedEntries.get(today).value, equalTo(100000))
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun testOnToggle() {
|
fun testOnToggle() {
|
||||||
assertTrue(habit1.isCompletedToday())
|
assertTrue(habit1.isCompletedToday())
|
||||||
behavior.onToggle(habit1, getToday(), Entry.NO)
|
behavior.onToggle(habit1, getToday(), Entry.NO, "")
|
||||||
assertFalse(habit1.isCompletedToday())
|
assertFalse(habit1.isCompletedToday())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user