diff --git a/uhabits-android/src/main/java/org/isoron/platform/gui/AndroidDataView.kt b/uhabits-android/src/main/java/org/isoron/platform/gui/AndroidDataView.kt index df5d5e184..0a8c5ee68 100644 --- a/uhabits-android/src/main/java/org/isoron/platform/gui/AndroidDataView.kt +++ b/uhabits-android/src/main/java/org/isoron/platform/gui/AndroidDataView.kt @@ -49,23 +49,12 @@ class AndroidDataView( override fun onShowPress(e: MotionEvent?) = Unit override fun onSingleTapUp(e: MotionEvent?): Boolean { - val x: Float - val y: Float - try { - val pointerId = e!!.getPointerId(0) - x = e.getX(pointerId) - y = e.getY(pointerId) - } catch (ex: RuntimeException) { - // Android often throws IllegalArgumentException here. Apparently, - // the pointer id may become invalid shortly after calling - // e.getPointerId. - return false - } - view?.onClick(x / canvas.innerDensity, y / canvas.innerDensity) - return true + return handleClick(e, true) } - override fun onLongPress(e: MotionEvent?) = Unit + override fun onLongPress(e: MotionEvent?) { + handleClick(e) + } override fun onScroll( e1: MotionEvent?, @@ -137,4 +126,22 @@ class AndroidDataView( } } } + + private fun handleClick(e: MotionEvent?, isSingleTap: Boolean = false): Boolean { + val x: Float + val y: Float + try { + val pointerId = e!!.getPointerId(0) + x = e.getX(pointerId) + y = e.getY(pointerId) + } catch (ex: RuntimeException) { + // Android often throws IllegalArgumentException here. Apparently, + // the pointer id may become invalid shortly after calling + // e.getPointerId. + return false + } + if (isSingleTap) view?.onClick(x / canvas.innerDensity, y / canvas.innerDensity) + else view?.onLongClick(x / canvas.innerDensity, y / canvas.innerDensity) + return true + } } 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 5edae637f..c040da2ac 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 @@ -63,9 +63,10 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener { paletteColor = habit.color, series = emptyList(), defaultSquare = HistoryChart.Square.OFF, + hasNotes = emptyList(), theme = themeSwitcher.currentTheme, today = DateUtils.getTodayWithOffset().toLocalDate(), - onDateClickedListener = onDateClickedListener ?: OnDateClickedListener { }, + onDateClickedListener = onDateClickedListener ?: OnDateClickedListener { _, _ -> }, padding = 10.0, ) dataView = AndroidDataView(context!!, null) @@ -103,6 +104,7 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener { ) chart?.series = model.series chart?.defaultSquare = model.defaultSquare + chart?.hasNotes = model.hasNotes dataView.postInvalidate() } 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 f429e1718..345789cca 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 @@ -44,6 +44,7 @@ class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(cont dateFormatter = JavaLocalDateFormatter(Locale.getDefault()), series = state.series, defaultSquare = state.defaultSquare, + hasNotes = state.hasNotes, 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 c8d4dce44..7d520bec5 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 @@ -59,6 +59,7 @@ class HistoryWidget( val historyChart = (this.view as HistoryChart) historyChart.series = model.series historyChart.defaultSquare = model.defaultSquare + historyChart.hasNotes = model.hasNotes } } @@ -74,6 +75,7 @@ class HistoryWidget( firstWeekday = prefs.firstWeekday, series = listOf(), defaultSquare = HistoryChart.Square.OFF, + hasNotes = listOf(), ) } ).apply { diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/platform/gui/View.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/platform/gui/View.kt index b58e1affe..c6ee60b65 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/platform/gui/View.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/platform/gui/View.kt @@ -23,6 +23,8 @@ interface View { fun draw(canvas: Canvas) fun onClick(x: Double, y: Double) { } + fun onLongClick(x: Double, y: Double) { + } } interface DataView : View { 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 899734956..66a3c214a 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 @@ -46,6 +46,7 @@ data class HistoryCardState( val firstWeekday: DayOfWeek, val series: List, val defaultSquare: HistoryChart.Square, + val hasNotes: List, val theme: Theme, val today: LocalDate, ) @@ -58,7 +59,7 @@ class HistoryCardPresenter( val screen: Screen, ) : OnDateClickedListener { - override fun onDateClicked(date: LocalDate) { + override fun onDateClicked(date: LocalDate, isLongClick: Boolean) { val timestamp = Timestamp.fromLocalDate(date) screen.showFeedback() if (habit.isNumerical) { @@ -81,20 +82,34 @@ class HistoryCardPresenter( val entry = habit.computedEntries.get(timestamp) val currentValue = entry.value val notes = entry.notes - val nextValue = Entry.nextToggleValue( - value = currentValue, - isSkipEnabled = preferences.isSkipEnabled, - areQuestionMarksEnabled = preferences.areQuestionMarksEnabled - ) - commandRunner.run( - CreateRepetitionCommand( - habitList, - habit, - timestamp, - nextValue, - notes, - ), - ) + if (!isLongClick) { + val nextValue = Entry.nextToggleValue( + value = currentValue, + isSkipEnabled = preferences.isSkipEnabled, + areQuestionMarksEnabled = preferences.areQuestionMarksEnabled + ) + commandRunner.run( + CreateRepetitionCommand( + habitList, + habit, + timestamp, + nextValue, + notes, + ), + ) + return + } + screen.showCheckmarkDialog(notes) { newNotes -> + commandRunner.run( + CreateRepetitionCommand( + habitList, + habit, + timestamp, + currentValue, + newNotes, + ), + ) + } } } @@ -142,6 +157,13 @@ class HistoryCardPresenter( else HistoryChart.Square.OFF + val hasNotes = entries.map { + when (it.notes) { + "" -> false + else -> true + } + } + return HistoryCardState( color = habit.color, firstWeekday = firstWeekday, @@ -149,6 +171,7 @@ class HistoryCardPresenter( theme = theme, series = series, defaultSquare = defaultSquare + hasNotes = hasNotes, ) } } 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 f0da7a196..150713056 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 @@ -33,7 +33,7 @@ import kotlin.math.min import kotlin.math.round fun interface OnDateClickedListener { - fun onDateClicked(date: LocalDate) + fun onDateClicked(date: LocalDate, isLongClick: Boolean) } class HistoryChart( @@ -42,9 +42,10 @@ class HistoryChart( var paletteColor: PaletteColor, var series: List, var defaultSquare: Square, + var hasNotes: List, var theme: Theme, var today: LocalDate, - var onDateClickedListener: OnDateClickedListener = OnDateClickedListener { }, + var onDateClickedListener: OnDateClickedListener = OnDateClickedListener { _, _ -> }, var padding: Double = 0.0, ) : DataView { @@ -72,6 +73,14 @@ class HistoryChart( get() = squareSpacing + squareSize override fun onClick(x: Double, y: Double) { + onDateClicked(x, y, false) + } + + override fun onLongClick(x: Double, y: Double) { + onDateClicked(x, y, true) + } + + private fun onDateClicked(x: Double, y: Double, isLongClick: Boolean) { if (width <= 0.0) throw IllegalStateException("onClick must be called after draw(canvas)") val col = ((x - padding) / squareSize).toInt() val row = ((y - padding) / squareSize).toInt() @@ -79,7 +88,7 @@ class HistoryChart( if (row == 0 || col == nColumns) return val clickedDate = topLeftDate.plus(offset) if (clickedDate.isNewerThan(today)) return - onDateClickedListener.onDateClicked(clickedDate) + onDateClickedListener.onDateClicked(clickedDate, isLongClick) } override fun draw(canvas: Canvas) { @@ -191,7 +200,9 @@ class HistoryChart( ) { val value = if (offset >= series.size) defaultSquare else series[offset] + val notes = if (offset >= hasNotes.size) false else hasNotes[offset] val squareColor: Color + val circleColor: Color val color = theme.color(paletteColor.paletteIndex) squareColor = when (value) { Square.ON -> { @@ -235,5 +246,14 @@ class HistoryChart( canvas.setColor(textColor) canvas.setTextAlign(TextAlign.CENTER) canvas.drawText(date.day.toString(), x + width / 2, y + width / 2) + + if (notes) { + circleColor = when (value) { + Square.ON -> theme.lowContrastTextColor + else -> color + } + canvas.setColor(circleColor) + canvas.fillCircle(x + width - width / 5, y + width / 5, width / 12) + } } }