mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Implement CheckmarkPopup
This commit is contained in:
@@ -36,6 +36,7 @@ class EntryButtonViewTest : BaseViewTest() {
|
|||||||
lateinit var view: CheckmarkButtonView
|
lateinit var view: CheckmarkButtonView
|
||||||
|
|
||||||
var toggled = false
|
var toggled = false
|
||||||
|
var edited = false
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
@@ -44,6 +45,7 @@ class EntryButtonViewTest : BaseViewTest() {
|
|||||||
value = Entry.NO
|
value = Entry.NO
|
||||||
color = PaletteUtils.getAndroidTestColor(5)
|
color = PaletteUtils.getAndroidTestColor(5)
|
||||||
onToggle = { _, _, _ -> toggled = true }
|
onToggle = { _, _, _ -> toggled = true }
|
||||||
|
onEdit = { _ -> edited = true }
|
||||||
}
|
}
|
||||||
measureView(view, dpToPixels(48), dpToPixels(48))
|
measureView(view, dpToPixels(48), dpToPixels(48))
|
||||||
}
|
}
|
||||||
@@ -70,20 +72,28 @@ class EntryButtonViewTest : BaseViewTest() {
|
|||||||
fun testClick_withShortToggleDisabled() {
|
fun testClick_withShortToggleDisabled() {
|
||||||
prefs.isShortToggleEnabled = false
|
prefs.isShortToggleEnabled = false
|
||||||
view.performClick()
|
view.performClick()
|
||||||
assertFalse(toggled)
|
assertTrue(!toggled and edited)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testClick_withShortToggleEnabled() {
|
fun testClick_withShortToggleEnabled() {
|
||||||
prefs.isShortToggleEnabled = true
|
prefs.isShortToggleEnabled = true
|
||||||
view.performClick()
|
view.performClick()
|
||||||
assertTrue(toggled)
|
assertTrue(toggled and !edited)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLongClick() {
|
fun testLongClick_withShortToggleDisabled() {
|
||||||
|
prefs.isShortToggleEnabled = false
|
||||||
view.performLongClick()
|
view.performLongClick()
|
||||||
assertTrue(toggled)
|
assertTrue(toggled and !edited)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLongClick_withShortToggleEnabled() {
|
||||||
|
prefs.isShortToggleEnabled = true
|
||||||
|
view.performLongClick()
|
||||||
|
assertTrue(!toggled and edited)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertRendersCheckedExplicitly() {
|
private fun assertRendersCheckedExplicitly() {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class NumberPanelViewTest : BaseViewTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun testEdit() {
|
fun testEdit() {
|
||||||
val timestamps = mutableListOf<Timestamp>()
|
val timestamps = mutableListOf<Timestamp>()
|
||||||
view.onEdit = { timestamps.plusAssign(it) }
|
view.onEdit = { _, t -> timestamps.plusAssign(t) }
|
||||||
view.buttons[0].performLongClick()
|
view.buttons[0].performLongClick()
|
||||||
view.buttons[2].performLongClick()
|
view.buttons[2].performLongClick()
|
||||||
view.buttons[3].performLongClick()
|
view.buttons[3].performLongClick()
|
||||||
@@ -87,7 +87,7 @@ class NumberPanelViewTest : BaseViewTest() {
|
|||||||
fun testEdit_withOffset() {
|
fun testEdit_withOffset() {
|
||||||
val timestamps = mutableListOf<Timestamp>()
|
val timestamps = mutableListOf<Timestamp>()
|
||||||
view.dataOffset = 3
|
view.dataOffset = 3
|
||||||
view.onEdit = { timestamps += it }
|
view.onEdit = { _, t -> timestamps += t }
|
||||||
view.buttons[0].performLongClick()
|
view.buttons[0].performLongClick()
|
||||||
view.buttons[2].performLongClick()
|
view.buttons[2].performLongClick()
|
||||||
view.buttons[3].performLongClick()
|
view.buttons[3].performLongClick()
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.common.dialogs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.widget.PopupWindow
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.SKIP
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||||
|
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.preferences.Preferences
|
||||||
|
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils.getFontAwesome
|
||||||
|
import org.isoron.uhabits.utils.dimBehind
|
||||||
|
import org.isoron.uhabits.utils.dp
|
||||||
|
import org.isoron.uhabits.utils.sres
|
||||||
|
|
||||||
|
const val POPUP_WIDTH = 4 * 48f + 16f
|
||||||
|
const val POPUP_HEIGHT = 48f * 2.5f + 8f
|
||||||
|
|
||||||
|
class CheckmarkPopup(
|
||||||
|
private val context: Context,
|
||||||
|
private val color: Int,
|
||||||
|
private var notes: String,
|
||||||
|
private var value: Int,
|
||||||
|
private val prefs: Preferences,
|
||||||
|
private val anchor: View,
|
||||||
|
) {
|
||||||
|
var onToggle: (Int, String) -> Unit = { _, _ -> }
|
||||||
|
|
||||||
|
private val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)).apply {
|
||||||
|
// Required for round corners
|
||||||
|
container.clipToOutline = true
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
initColors()
|
||||||
|
initTypefaces()
|
||||||
|
hideDisabledButtons()
|
||||||
|
populate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initColors() {
|
||||||
|
arrayOf(view.yesBtn, view.skipBtn).forEach {
|
||||||
|
it.setTextColor(color)
|
||||||
|
}
|
||||||
|
arrayOf(view.noBtn, view.unknownBtn).forEach {
|
||||||
|
it.setTextColor(view.root.sres.getColor(R.attr.contrast60))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initTypefaces() {
|
||||||
|
arrayOf(view.yesBtn, view.noBtn, view.skipBtn, view.unknownBtn).forEach {
|
||||||
|
it.typeface = getFontAwesome(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideDisabledButtons() {
|
||||||
|
if (!prefs.isSkipEnabled) view.skipBtn.visibility = GONE
|
||||||
|
if (!prefs.areQuestionMarksEnabled) view.unknownBtn.visibility = GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populate() {
|
||||||
|
val selectedBtn = when (value) {
|
||||||
|
YES_MANUAL -> view.yesBtn
|
||||||
|
YES_AUTO -> view.noBtn
|
||||||
|
NO -> view.noBtn
|
||||||
|
UNKNOWN -> if (prefs.areQuestionMarksEnabled) view.unknownBtn else view.noBtn
|
||||||
|
SKIP -> if (prefs.isSkipEnabled) view.skipBtn else view.noBtn
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
selectedBtn?.background = ColorDrawable(view.root.sres.getColor(R.attr.contrast40))
|
||||||
|
view.notes.setText(notes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show(location: ScreenLocation) {
|
||||||
|
val popup = PopupWindow()
|
||||||
|
popup.contentView = view.root
|
||||||
|
popup.width = view.root.dp(POPUP_WIDTH).toInt()
|
||||||
|
popup.height = view.root.dp(POPUP_HEIGHT).toInt()
|
||||||
|
popup.isFocusable = true
|
||||||
|
popup.elevation = view.root.dp(24f)
|
||||||
|
fun onClick(v: Int) {
|
||||||
|
this.value = v
|
||||||
|
popup.dismiss()
|
||||||
|
}
|
||||||
|
view.yesBtn.setOnClickListener { onClick(YES_MANUAL) }
|
||||||
|
view.noBtn.setOnClickListener { onClick(NO) }
|
||||||
|
view.skipBtn.setOnClickListener { onClick(SKIP) }
|
||||||
|
view.unknownBtn.setOnClickListener { onClick(UNKNOWN) }
|
||||||
|
popup.setOnDismissListener {
|
||||||
|
onToggle(value, view.notes.text.toString())
|
||||||
|
}
|
||||||
|
popup.showAtLocation(
|
||||||
|
anchor,
|
||||||
|
Gravity.NO_GRAVITY,
|
||||||
|
view.root.dp(location.x.toFloat()).toInt(),
|
||||||
|
view.root.dp(location.y.toFloat()).toInt(),
|
||||||
|
)
|
||||||
|
popup.dimBehind()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
|
|||||||
private lateinit var commandRunner: CommandRunner
|
private lateinit var commandRunner: CommandRunner
|
||||||
private lateinit var habit: Habit
|
private lateinit var habit: Habit
|
||||||
private lateinit var preferences: Preferences
|
private lateinit var preferences: Preferences
|
||||||
private lateinit var dataView: AndroidDataView
|
lateinit var dataView: AndroidDataView
|
||||||
|
|
||||||
private var chart: HistoryChart? = null
|
private var chart: HistoryChart? = null
|
||||||
private var onDateClickedListener: OnDateClickedListener? = null
|
private var onDateClickedListener: OnDateClickedListener? = null
|
||||||
|
|||||||
@@ -24,12 +24,16 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.platform.time.LocalDate
|
import org.isoron.platform.time.LocalDate
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
|
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.CheckmarkPopup
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.POPUP_WIDTH
|
||||||
import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog
|
import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog
|
||||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||||
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
|
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
|
||||||
@@ -43,6 +47,7 @@ import org.isoron.uhabits.core.commands.UnarchiveHabitsCommand
|
|||||||
import org.isoron.uhabits.core.models.Frequency
|
import org.isoron.uhabits.core.models.Frequency
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||||
import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback
|
import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback
|
||||||
@@ -63,6 +68,7 @@ import org.isoron.uhabits.tasks.ExportDBTaskFactory
|
|||||||
import org.isoron.uhabits.tasks.ImportDataTask
|
import org.isoron.uhabits.tasks.ImportDataTask
|
||||||
import org.isoron.uhabits.tasks.ImportDataTaskFactory
|
import org.isoron.uhabits.tasks.ImportDataTaskFactory
|
||||||
import org.isoron.uhabits.utils.copyTo
|
import org.isoron.uhabits.utils.copyTo
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.restartWithFade
|
import org.isoron.uhabits.utils.restartWithFade
|
||||||
import org.isoron.uhabits.utils.showMessage
|
import org.isoron.uhabits.utils.showMessage
|
||||||
import org.isoron.uhabits.utils.showSendEmailScreen
|
import org.isoron.uhabits.utils.showSendEmailScreen
|
||||||
@@ -93,7 +99,9 @@ class ListHabitsScreen
|
|||||||
private val colorPickerFactory: ColorPickerDialogFactory,
|
private val colorPickerFactory: ColorPickerDialogFactory,
|
||||||
private val numberPickerFactory: NumberPickerFactory,
|
private val numberPickerFactory: NumberPickerFactory,
|
||||||
private val checkMarkDialog: CheckmarkDialog,
|
private val checkMarkDialog: CheckmarkDialog,
|
||||||
private val behavior: Lazy<ListHabitsBehavior>
|
private val behavior: Lazy<ListHabitsBehavior>,
|
||||||
|
private val preferences: Preferences,
|
||||||
|
private val rootView: Lazy<ListHabitsRootView>,
|
||||||
) : CommandRunner.Listener,
|
) : CommandRunner.Listener,
|
||||||
ListHabitsBehavior.Screen,
|
ListHabitsBehavior.Screen,
|
||||||
ListHabitsMenuBehavior.Screen,
|
ListHabitsMenuBehavior.Screen,
|
||||||
@@ -237,6 +245,32 @@ class ListHabitsScreen
|
|||||||
numberPickerFactory.create(value, unit, notes, dateString, frequency, callback).show()
|
numberPickerFactory.create(value, unit, notes, dateString, frequency, callback).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showCheckmarkPopup(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
color: PaletteColor,
|
||||||
|
location: ScreenLocation,
|
||||||
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
|
) {
|
||||||
|
val view = rootView.get()
|
||||||
|
CheckmarkPopup(
|
||||||
|
context = context,
|
||||||
|
prefs = preferences,
|
||||||
|
anchor = view,
|
||||||
|
color = view.currentTheme().color(color).toInt(),
|
||||||
|
notes = notes,
|
||||||
|
value = selectedValue,
|
||||||
|
).apply {
|
||||||
|
onToggle = { value, notes -> callback.onNotesSaved(value, notes) }
|
||||||
|
show(
|
||||||
|
ScreenLocation(
|
||||||
|
x = location.x - POPUP_WIDTH / 2,
|
||||||
|
y = location.y
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun showCheckmarkDialog(
|
override fun showCheckmarkDialog(
|
||||||
selectedValue: Int,
|
selectedValue: Int,
|
||||||
notes: String,
|
notes: String,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.text.TextPaint
|
|||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.MeasureSpec.EXACTLY
|
import android.view.View.MeasureSpec.EXACTLY
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.Entry
|
import org.isoron.uhabits.core.models.Entry
|
||||||
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||||
@@ -38,6 +39,7 @@ import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
|||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.utils.drawNotesIndicator
|
import org.isoron.uhabits.utils.drawNotesIndicator
|
||||||
|
import org.isoron.uhabits.utils.getCenter
|
||||||
import org.isoron.uhabits.utils.getFontAwesome
|
import org.isoron.uhabits.utils.getFontAwesome
|
||||||
import org.isoron.uhabits.utils.sp
|
import org.isoron.uhabits.utils.sp
|
||||||
import org.isoron.uhabits.utils.sres
|
import org.isoron.uhabits.utils.sres
|
||||||
@@ -81,7 +83,8 @@ class CheckmarkButtonView(
|
|||||||
|
|
||||||
var onToggle: (Int, String, Long) -> Unit = { _, _, _ -> }
|
var onToggle: (Int, String, Long) -> Unit = { _, _, _ -> }
|
||||||
|
|
||||||
var onEdit: () -> Unit = {}
|
var onEdit: (ScreenLocation) -> Unit = { _ -> }
|
||||||
|
|
||||||
private var drawer = Drawer()
|
private var drawer = Drawer()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -102,11 +105,11 @@ class CheckmarkButtonView(
|
|||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
if (preferences.isShortToggleEnabled) performToggle(TOGGLE_DELAY_MILLIS)
|
if (preferences.isShortToggleEnabled) performToggle(TOGGLE_DELAY_MILLIS)
|
||||||
else onEdit()
|
else onEdit(getCenter())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View): Boolean {
|
override fun onLongClick(v: View): Boolean {
|
||||||
if (preferences.isShortToggleEnabled) onEdit()
|
if (preferences.isShortToggleEnabled) onEdit(getCenter())
|
||||||
else performToggle(TOGGLE_DELAY_MILLIS)
|
else performToggle(TOGGLE_DELAY_MILLIS)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package org.isoron.uhabits.activities.habits.list.views
|
package org.isoron.uhabits.activities.habits.list.views
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
@@ -66,7 +67,7 @@ class CheckmarkPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
var onEdit: (Timestamp) -> Unit = {}
|
var onEdit: (ScreenLocation, Timestamp) -> Unit = { _, _ -> }
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
setupButtons()
|
setupButtons()
|
||||||
@@ -90,7 +91,7 @@ class CheckmarkPanelView(
|
|||||||
}
|
}
|
||||||
button.color = color
|
button.color = color
|
||||||
button.onToggle = { value, notes, delay -> onToggle(timestamp, value, notes, delay) }
|
button.onToggle = { value, notes, delay -> onToggle(timestamp, value, notes, delay) }
|
||||||
button.onEdit = { onEdit(timestamp) }
|
button.onEdit = { location -> onEdit(location, timestamp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,17 +167,17 @@ class HabitCardView(
|
|||||||
{ runPendingToggles(taskId) }.delay(delay)
|
{ runPendingToggles(taskId) }.delay(delay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onEdit = { timestamp ->
|
onEdit = { location, timestamp ->
|
||||||
triggerRipple(timestamp)
|
triggerRipple(timestamp)
|
||||||
habit?.let { behavior.onEdit(it, timestamp) }
|
habit?.let { behavior.onEdit(location, it, timestamp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
numberPanel = numberPanelFactory.create().apply {
|
numberPanel = numberPanelFactory.create().apply {
|
||||||
visibility = GONE
|
visibility = GONE
|
||||||
onEdit = { timestamp ->
|
onEdit = { location, timestamp ->
|
||||||
triggerRipple(timestamp)
|
triggerRipple(timestamp)
|
||||||
habit?.let { behavior.onEdit(it, timestamp) }
|
habit?.let { behavior.onEdit(location, it, timestamp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,13 @@
|
|||||||
package org.isoron.uhabits.activities.habits.list.views
|
package org.isoron.uhabits.activities.habits.list.views
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.uhabits.core.models.NumericalHabitType
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
|
import org.isoron.uhabits.utils.getCenter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NumberPanelViewFactory
|
class NumberPanelViewFactory
|
||||||
@@ -78,7 +80,7 @@ class NumberPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
var onEdit: (Timestamp) -> Unit = {}
|
var onEdit: (ScreenLocation, Timestamp) -> Unit = { _, _ -> }
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
setupButtons()
|
setupButtons()
|
||||||
@@ -104,7 +106,7 @@ class NumberPanelView(
|
|||||||
button.targetType = targetType
|
button.targetType = targetType
|
||||||
button.threshold = threshold
|
button.threshold = threshold
|
||||||
button.units = units
|
button.units = units
|
||||||
button.onEdit = { onEdit(timestamp) }
|
button.onEdit = { onEdit(getCenter(), timestamp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.platform.time.LocalDate
|
import org.isoron.platform.time.LocalDate
|
||||||
import org.isoron.uhabits.AndroidDirFinder
|
import org.isoron.uhabits.AndroidDirFinder
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
@@ -34,9 +36,11 @@ import org.isoron.uhabits.R
|
|||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.activities.HabitsDirFinder
|
import org.isoron.uhabits.activities.HabitsDirFinder
|
||||||
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
|
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.CheckmarkPopup
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog
|
import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.POPUP_WIDTH
|
||||||
import org.isoron.uhabits.core.commands.Command
|
import org.isoron.uhabits.core.commands.Command
|
||||||
import org.isoron.uhabits.core.commands.CommandRunner
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
import org.isoron.uhabits.core.models.Frequency
|
import org.isoron.uhabits.core.models.Frequency
|
||||||
@@ -49,6 +53,8 @@ import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuPresenter
|
|||||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitPresenter
|
||||||
import org.isoron.uhabits.core.ui.views.OnDateClickedListener
|
import org.isoron.uhabits.core.ui.views.OnDateClickedListener
|
||||||
import org.isoron.uhabits.intents.IntentFactory
|
import org.isoron.uhabits.intents.IntentFactory
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
|
import org.isoron.uhabits.utils.getTopLeftCorner
|
||||||
import org.isoron.uhabits.utils.showMessage
|
import org.isoron.uhabits.utils.showMessage
|
||||||
import org.isoron.uhabits.utils.showSendFileScreen
|
import org.isoron.uhabits.utils.showSendFileScreen
|
||||||
import org.isoron.uhabits.widgets.WidgetUpdater
|
import org.isoron.uhabits.widgets.WidgetUpdater
|
||||||
@@ -173,7 +179,14 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
|||||||
frequency: Frequency,
|
frequency: Frequency,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback
|
callback: ListHabitsBehavior.NumberPickerCallback
|
||||||
) {
|
) {
|
||||||
NumberPickerFactory(this@ShowHabitActivity).create(value, unit, notes, dateString, frequency, callback).show()
|
NumberPickerFactory(this@ShowHabitActivity).create(
|
||||||
|
value,
|
||||||
|
unit,
|
||||||
|
notes,
|
||||||
|
dateString,
|
||||||
|
frequency,
|
||||||
|
callback
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showCheckmarkDialog(
|
override fun showCheckmarkDialog(
|
||||||
@@ -194,6 +207,37 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showCheckmarkPopup(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
preferences: Preferences,
|
||||||
|
color: PaletteColor,
|
||||||
|
location: ScreenLocation,
|
||||||
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
|
) {
|
||||||
|
val dialog =
|
||||||
|
supportFragmentManager.findFragmentByTag("historyEditor") as HistoryEditorDialog?
|
||||||
|
?: return
|
||||||
|
val view = dialog.dataView
|
||||||
|
val corner = view.getTopLeftCorner()
|
||||||
|
CheckmarkPopup(
|
||||||
|
context = this@ShowHabitActivity,
|
||||||
|
prefs = preferences,
|
||||||
|
notes = notes,
|
||||||
|
color = view.currentTheme().color(color).toInt(),
|
||||||
|
anchor = view,
|
||||||
|
value = selectedValue,
|
||||||
|
).apply {
|
||||||
|
onToggle = { v, n -> callback.onNotesSaved(v, n) }
|
||||||
|
show(
|
||||||
|
ScreenLocation(
|
||||||
|
x = corner.x + location.x - POPUP_WIDTH / 2,
|
||||||
|
y = corner.y + location.y,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun showEditHabitScreen(habit: Habit) {
|
override fun showEditHabitScreen(habit: Habit) {
|
||||||
startActivity(IntentFactory().startEditActivity(this@ShowHabitActivity, habit))
|
startActivity(IntentFactory().startEditActivity(this@ShowHabitActivity, habit))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package org.isoron.uhabits.utils
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
@@ -32,6 +33,8 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.PopupWindow
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM
|
import android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM
|
||||||
import android.widget.RelativeLayout.ALIGN_PARENT_TOP
|
import android.widget.RelativeLayout.ALIGN_PARENT_TOP
|
||||||
@@ -42,6 +45,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.platform.gui.toInt
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
@@ -213,3 +217,42 @@ fun View.drawNotesIndicator(canvas: Canvas, color: Int, size: Float, notes: Stri
|
|||||||
|
|
||||||
val View.sres: StyledResources
|
val View.sres: StyledResources
|
||||||
get() = StyledResources(context)
|
get() = StyledResources(context)
|
||||||
|
|
||||||
|
fun PopupWindow.dimBehind() {
|
||||||
|
// https://stackoverflow.com/questions/35874001/dim-the-background-using-popupwindow-in-android
|
||||||
|
val container = contentView.rootView
|
||||||
|
val context = contentView.context
|
||||||
|
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
val p = container.layoutParams as WindowManager.LayoutParams
|
||||||
|
p.flags = p.flags or WindowManager.LayoutParams.FLAG_DIM_BEHIND
|
||||||
|
p.dimAmount = 0.5f
|
||||||
|
wm.updateViewLayout(container, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute screen coordinates for the center of this view (in density-independent
|
||||||
|
* pixels).
|
||||||
|
*/
|
||||||
|
fun View.getCenter(): ScreenLocation {
|
||||||
|
val density = resources.displayMetrics.density
|
||||||
|
val loc = IntArray(2)
|
||||||
|
this.getLocationInWindow(loc)
|
||||||
|
return ScreenLocation(
|
||||||
|
x = ((loc[0] + width / 2) / density).toDouble(),
|
||||||
|
y = ((loc[1] + height / 2) / density).toDouble(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute screen coordinates for the top left corner of this view (in
|
||||||
|
* density-independent pixels).
|
||||||
|
*/
|
||||||
|
fun View.getTopLeftCorner(): ScreenLocation {
|
||||||
|
val density = resources.displayMetrics.density
|
||||||
|
val loc = IntArray(2)
|
||||||
|
this.getLocationInWindow(loc)
|
||||||
|
return ScreenLocation(
|
||||||
|
x = (loc[0] / density).toDouble(),
|
||||||
|
y = (loc[1] / density).toDouble(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="?attr/contrast0" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="?contrast40" />
|
||||||
|
<corners android:radius="5dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<size android:width="1dip"/>
|
||||||
|
<size android:height="1dip"/>
|
||||||
|
<solid android:color="?contrast40"/>
|
||||||
|
</shape>
|
||||||
72
uhabits-android/src/main/res/layout/checkmark_popup.xml
Normal file
72
uhabits-android/src/main/res/layout/checkmark_popup.xml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:divider="@drawable/checkmark_dialog_divider"
|
||||||
|
app:showDividers="middle"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/checkmark_dialog_bg">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatEditText
|
||||||
|
android:id="@+id/notes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:inputType="textCapSentences|textMultiLine"
|
||||||
|
android:textSize="@dimen/smallTextSize"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:hint="@string/notes"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:divider="@drawable/checkmark_dialog_divider"
|
||||||
|
app:showDividers="middle">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yesBtn"
|
||||||
|
style="@style/CheckmarkPopupBtn"
|
||||||
|
android:text="@string/fa_check" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/skipBtn"
|
||||||
|
style="@style/CheckmarkPopupBtn"
|
||||||
|
android:text="@string/fa_skipped" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/noBtn"
|
||||||
|
style="@style/CheckmarkPopupBtn"
|
||||||
|
android:text="@string/fa_times" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/unknownBtn"
|
||||||
|
style="@style/CheckmarkPopupBtn"
|
||||||
|
android:text="@string/fa_question" />
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
@@ -399,4 +399,15 @@
|
|||||||
<item name="selectable">true</item>
|
<item name="selectable">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="CheckmarkPopupBtn">
|
||||||
|
<item name="android:layout_width">0dp</item>
|
||||||
|
<item name="android:layout_weight">1</item>
|
||||||
|
<item name="android:layout_height">match_parent</item>
|
||||||
|
<item name="android:gravity">center</item>
|
||||||
|
<item name="android:clickable">true</item>
|
||||||
|
<item name="android:focusable">true</item>
|
||||||
|
<item name="android:background">@drawable/ripple_transparent</item>
|
||||||
|
<item name="android:textSize">@dimen/smallerTextSize</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ enum class Font {
|
|||||||
FONT_AWESOME
|
FONT_AWESOME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ScreenLocation(
|
||||||
|
val x: Double,
|
||||||
|
val y: Double,
|
||||||
|
)
|
||||||
|
|
||||||
interface Canvas {
|
interface Canvas {
|
||||||
fun setColor(color: Color)
|
fun setColor(color: Color)
|
||||||
fun drawLine(x1: Double, y1: Double, x2: Double, y2: Double)
|
fun drawLine(x1: Double, y1: Double, x2: Double, y2: Double)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.isoron.uhabits.core.ui.screens.habits.list
|
package org.isoron.uhabits.core.ui.screens.habits.list
|
||||||
|
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.platform.time.LocalDate
|
import org.isoron.platform.time.LocalDate
|
||||||
import org.isoron.uhabits.core.commands.CommandRunner
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
||||||
@@ -50,7 +51,7 @@ open class ListHabitsBehavior @Inject constructor(
|
|||||||
screen.showHabitScreen(h)
|
screen.showHabitScreen(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEdit(habit: Habit, timestamp: Timestamp?) {
|
fun onEdit(location: ScreenLocation, habit: Habit, timestamp: Timestamp?) {
|
||||||
val entry = habit.computedEntries.get(timestamp!!)
|
val entry = habit.computedEntries.get(timestamp!!)
|
||||||
if (habit.type == HabitType.NUMERICAL) {
|
if (habit.type == HabitType.NUMERICAL) {
|
||||||
val oldValue = entry.value.toDouble()
|
val oldValue = entry.value.toDouble()
|
||||||
@@ -65,12 +66,11 @@ open class ListHabitsBehavior @Inject constructor(
|
|||||||
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, value, newNotes))
|
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, value, newNotes))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
screen.showCheckmarkDialog(
|
screen.showCheckmarkPopup(
|
||||||
entry.value,
|
entry.value,
|
||||||
entry.notes,
|
entry.notes,
|
||||||
timestamp.toLocalDate(),
|
|
||||||
timestamp.toDialogDateString(),
|
|
||||||
habit.color,
|
habit.color,
|
||||||
|
location,
|
||||||
) { newValue, newNotes ->
|
) { newValue, newNotes ->
|
||||||
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, newValue, newNotes))
|
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, newValue, newNotes))
|
||||||
}
|
}
|
||||||
@@ -171,6 +171,13 @@ open class ListHabitsBehavior @Inject constructor(
|
|||||||
frequency: Frequency,
|
frequency: Frequency,
|
||||||
callback: NumberPickerCallback
|
callback: NumberPickerCallback
|
||||||
)
|
)
|
||||||
|
fun showCheckmarkPopup(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
color: PaletteColor,
|
||||||
|
location: ScreenLocation,
|
||||||
|
callback: CheckMarkDialogCallback
|
||||||
|
)
|
||||||
fun showCheckmarkDialog(
|
fun showCheckmarkDialog(
|
||||||
selectedValue: Int,
|
selectedValue: Int,
|
||||||
notes: String,
|
notes: String,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.core.ui.screens.habits.show.views
|
package org.isoron.uhabits.core.ui.screens.habits.show.views
|
||||||
|
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.platform.time.DayOfWeek
|
import org.isoron.platform.time.DayOfWeek
|
||||||
import org.isoron.platform.time.LocalDate
|
import org.isoron.platform.time.LocalDate
|
||||||
import org.isoron.uhabits.core.commands.CommandRunner
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
@@ -65,55 +66,65 @@ class HistoryCardPresenter(
|
|||||||
val screen: Screen,
|
val screen: Screen,
|
||||||
) : OnDateClickedListener {
|
) : OnDateClickedListener {
|
||||||
|
|
||||||
override fun onDateLongPress(date: LocalDate) {
|
override fun onDateLongPress(location: ScreenLocation, date: LocalDate) {
|
||||||
val timestamp = Timestamp.fromLocalDate(date)
|
val timestamp = Timestamp.fromLocalDate(date)
|
||||||
screen.showFeedback()
|
screen.showFeedback()
|
||||||
if (habit.isNumerical) {
|
if (habit.isNumerical) {
|
||||||
showNumberPicker(timestamp)
|
showNumberPicker(timestamp)
|
||||||
} else {
|
} else {
|
||||||
val entry = habit.computedEntries.get(timestamp)
|
if (preferences.isShortToggleEnabled) showCheckmarkPopup(location, timestamp)
|
||||||
val nextValue = Entry.nextToggleValue(
|
else toggle(timestamp)
|
||||||
value = entry.value,
|
}
|
||||||
isSkipEnabled = preferences.isSkipEnabled,
|
}
|
||||||
areQuestionMarksEnabled = preferences.areQuestionMarksEnabled
|
|
||||||
)
|
override fun onDateShortPress(location: ScreenLocation, date: LocalDate) {
|
||||||
|
val timestamp = Timestamp.fromLocalDate(date)
|
||||||
|
screen.showFeedback()
|
||||||
|
if (habit.isNumerical) {
|
||||||
|
showNumberPicker(timestamp)
|
||||||
|
} else {
|
||||||
|
if (preferences.isShortToggleEnabled) toggle(timestamp)
|
||||||
|
else showCheckmarkPopup(location, timestamp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showCheckmarkPopup(location: ScreenLocation, timestamp: Timestamp) {
|
||||||
|
val entry = habit.computedEntries.get(timestamp)
|
||||||
|
screen.showCheckmarkPopup(
|
||||||
|
entry.value,
|
||||||
|
entry.notes,
|
||||||
|
preferences,
|
||||||
|
habit.color,
|
||||||
|
location,
|
||||||
|
) { newValue, newNotes ->
|
||||||
commandRunner.run(
|
commandRunner.run(
|
||||||
CreateRepetitionCommand(
|
CreateRepetitionCommand(
|
||||||
habitList,
|
habitList,
|
||||||
habit,
|
habit,
|
||||||
timestamp,
|
timestamp,
|
||||||
nextValue,
|
newValue,
|
||||||
entry.notes,
|
newNotes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDateShortPress(date: LocalDate) {
|
private fun toggle(timestamp: Timestamp) {
|
||||||
val timestamp = Timestamp.fromLocalDate(date)
|
val entry = habit.computedEntries.get(timestamp)
|
||||||
screen.showFeedback()
|
val nextValue = Entry.nextToggleValue(
|
||||||
if (habit.isNumerical) {
|
value = entry.value,
|
||||||
showNumberPicker(timestamp)
|
isSkipEnabled = preferences.isSkipEnabled,
|
||||||
} else {
|
areQuestionMarksEnabled = preferences.areQuestionMarksEnabled
|
||||||
val entry = habit.computedEntries.get(timestamp)
|
)
|
||||||
screen.showCheckmarkDialog(
|
commandRunner.run(
|
||||||
entry.value,
|
CreateRepetitionCommand(
|
||||||
|
habitList,
|
||||||
|
habit,
|
||||||
|
timestamp,
|
||||||
|
nextValue,
|
||||||
entry.notes,
|
entry.notes,
|
||||||
timestamp.toLocalDate(),
|
),
|
||||||
preferences,
|
)
|
||||||
habit.color,
|
|
||||||
) { newValue, newNotes ->
|
|
||||||
commandRunner.run(
|
|
||||||
CreateRepetitionCommand(
|
|
||||||
habitList,
|
|
||||||
habit,
|
|
||||||
timestamp,
|
|
||||||
newValue,
|
|
||||||
newNotes,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNumberPicker(timestamp: Timestamp) {
|
private fun showNumberPicker(timestamp: Timestamp) {
|
||||||
@@ -211,5 +222,14 @@ class HistoryCardPresenter(
|
|||||||
color: PaletteColor,
|
color: PaletteColor,
|
||||||
callback: ListHabitsBehavior.CheckMarkDialogCallback,
|
callback: ListHabitsBehavior.CheckMarkDialogCallback,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun showCheckmarkPopup(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
preferences: Preferences,
|
||||||
|
color: PaletteColor,
|
||||||
|
location: ScreenLocation,
|
||||||
|
callback: ListHabitsBehavior.CheckMarkDialogCallback,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package org.isoron.uhabits.core.ui.views
|
|||||||
import org.isoron.platform.gui.Canvas
|
import org.isoron.platform.gui.Canvas
|
||||||
import org.isoron.platform.gui.Color
|
import org.isoron.platform.gui.Color
|
||||||
import org.isoron.platform.gui.DataView
|
import org.isoron.platform.gui.DataView
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.platform.gui.TextAlign
|
import org.isoron.platform.gui.TextAlign
|
||||||
import org.isoron.platform.time.DayOfWeek
|
import org.isoron.platform.time.DayOfWeek
|
||||||
import org.isoron.platform.time.LocalDate
|
import org.isoron.platform.time.LocalDate
|
||||||
@@ -33,8 +34,8 @@ import kotlin.math.min
|
|||||||
import kotlin.math.round
|
import kotlin.math.round
|
||||||
|
|
||||||
interface OnDateClickedListener {
|
interface OnDateClickedListener {
|
||||||
fun onDateShortPress(date: LocalDate) {}
|
fun onDateShortPress(location: ScreenLocation, date: LocalDate) {}
|
||||||
fun onDateLongPress(date: LocalDate) {}
|
fun onDateLongPress(location: ScreenLocation, date: LocalDate) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HistoryChart(
|
class HistoryChart(
|
||||||
@@ -90,10 +91,11 @@ class HistoryChart(
|
|||||||
if (x - padding < 0 || row == 0 || row > 7 || col == nColumns) return
|
if (x - padding < 0 || row == 0 || row > 7 || col == nColumns) return
|
||||||
val clickedDate = topLeftDate.plus(offset)
|
val clickedDate = topLeftDate.plus(offset)
|
||||||
if (clickedDate.isNewerThan(today)) return
|
if (clickedDate.isNewerThan(today)) return
|
||||||
|
val location = ScreenLocation(x, y)
|
||||||
if (isLongClick) {
|
if (isLongClick) {
|
||||||
onDateClickedListener.onDateLongPress(clickedDate)
|
onDateClickedListener.onDateLongPress(location, clickedDate)
|
||||||
} else {
|
} else {
|
||||||
onDateClickedListener.onDateShortPress(clickedDate)
|
onDateClickedListener.onDateShortPress(location, clickedDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import junit.framework.Assert.assertTrue
|
|||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.core.IsEqual.equalTo
|
import org.hamcrest.core.IsEqual.equalTo
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.uhabits.core.BaseUnitTest
|
import org.isoron.uhabits.core.BaseUnitTest
|
||||||
import org.isoron.uhabits.core.models.Entry
|
import org.isoron.uhabits.core.models.Entry
|
||||||
import org.isoron.uhabits.core.models.Frequency
|
import org.isoron.uhabits.core.models.Frequency
|
||||||
@@ -79,7 +80,7 @@ class ListHabitsBehaviorTest : BaseUnitTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOnEdit() {
|
fun testOnEdit() {
|
||||||
behavior.onEdit(habit2, getToday())
|
behavior.onEdit(ScreenLocation(0.0, 0.0), habit2, getToday())
|
||||||
verify(screen).showNumberPicker(
|
verify(screen).showNumberPicker(
|
||||||
eq(0.1),
|
eq(0.1),
|
||||||
eq("miles"),
|
eq("miles"),
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.nhaarman.mockitokotlin2.reset
|
|||||||
import com.nhaarman.mockitokotlin2.verify
|
import com.nhaarman.mockitokotlin2.verify
|
||||||
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.isoron.platform.gui.ScreenLocation
|
||||||
import org.isoron.platform.gui.assertRenders
|
import org.isoron.platform.gui.assertRenders
|
||||||
import org.isoron.platform.time.DayOfWeek
|
import org.isoron.platform.time.DayOfWeek
|
||||||
import org.isoron.platform.time.DayOfWeek.SUNDAY
|
import org.isoron.platform.time.DayOfWeek.SUNDAY
|
||||||
@@ -73,8 +74,7 @@ class HistoryChartTest {
|
|||||||
else -> OFF
|
else -> OFF
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
notesIndicators = MutableList(85) {
|
notesIndicators = MutableList(85) { index: Int ->
|
||||||
index: Int ->
|
|
||||||
index % 3 == 0
|
index % 3 == 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -90,20 +90,32 @@ class HistoryChartTest {
|
|||||||
|
|
||||||
// Click top left date
|
// Click top left date
|
||||||
view.onClick(20.0, 46.0)
|
view.onClick(20.0, 46.0)
|
||||||
verify(dateClickedListener).onDateShortPress(LocalDate(2014, 10, 26))
|
verify(dateClickedListener).onDateShortPress(
|
||||||
|
ScreenLocation(20.0, 46.0),
|
||||||
|
LocalDate(2014, 10, 26)
|
||||||
|
)
|
||||||
reset(dateClickedListener)
|
reset(dateClickedListener)
|
||||||
view.onClick(2.0, 28.0)
|
view.onClick(2.0, 28.0)
|
||||||
verify(dateClickedListener).onDateShortPress(LocalDate(2014, 10, 26))
|
verify(dateClickedListener).onDateShortPress(
|
||||||
|
ScreenLocation(2.0, 28.0),
|
||||||
|
LocalDate(2014, 10, 26)
|
||||||
|
)
|
||||||
reset(dateClickedListener)
|
reset(dateClickedListener)
|
||||||
|
|
||||||
// Click date in the middle
|
// Click date in the middle
|
||||||
view.onClick(163.0, 113.0)
|
view.onClick(163.0, 113.0)
|
||||||
verify(dateClickedListener).onDateShortPress(LocalDate(2014, 12, 10))
|
verify(dateClickedListener).onDateShortPress(
|
||||||
|
ScreenLocation(163.0, 113.0),
|
||||||
|
LocalDate(2014, 12, 10)
|
||||||
|
)
|
||||||
reset(dateClickedListener)
|
reset(dateClickedListener)
|
||||||
|
|
||||||
// Click today
|
// Click today
|
||||||
view.onClick(336.0, 37.0)
|
view.onClick(336.0, 37.0)
|
||||||
verify(dateClickedListener).onDateShortPress(LocalDate(2015, 1, 25))
|
verify(dateClickedListener).onDateShortPress(
|
||||||
|
ScreenLocation(336.0, 37.0),
|
||||||
|
LocalDate(2015, 1, 25)
|
||||||
|
)
|
||||||
reset(dateClickedListener)
|
reset(dateClickedListener)
|
||||||
|
|
||||||
// Click header
|
// Click header
|
||||||
@@ -121,20 +133,32 @@ class HistoryChartTest {
|
|||||||
|
|
||||||
// Click top left date
|
// Click top left date
|
||||||
view.onLongClick(20.0, 46.0)
|
view.onLongClick(20.0, 46.0)
|
||||||
verify(dateClickedListener).onDateLongPress(LocalDate(2014, 10, 26))
|
verify(dateClickedListener).onDateLongPress(
|
||||||
|
ScreenLocation(20.0, 46.0),
|
||||||
|
LocalDate(2014, 10, 26)
|
||||||
|
)
|
||||||
reset(dateClickedListener)
|
reset(dateClickedListener)
|
||||||
view.onLongClick(2.0, 28.0)
|
view.onLongClick(2.0, 28.0)
|
||||||
verify(dateClickedListener).onDateLongPress(LocalDate(2014, 10, 26))
|
verify(dateClickedListener).onDateLongPress(
|
||||||
|
ScreenLocation(2.0, 28.0),
|
||||||
|
LocalDate(2014, 10, 26)
|
||||||
|
)
|
||||||
reset(dateClickedListener)
|
reset(dateClickedListener)
|
||||||
|
|
||||||
// Click date in the middle
|
// Click date in the middle
|
||||||
view.onLongClick(163.0, 113.0)
|
view.onLongClick(163.0, 113.0)
|
||||||
verify(dateClickedListener).onDateLongPress(LocalDate(2014, 12, 10))
|
verify(dateClickedListener).onDateLongPress(
|
||||||
|
ScreenLocation(163.0, 113.0),
|
||||||
|
LocalDate(2014, 12, 10)
|
||||||
|
)
|
||||||
reset(dateClickedListener)
|
reset(dateClickedListener)
|
||||||
|
|
||||||
// Click today
|
// Click today
|
||||||
view.onLongClick(336.0, 37.0)
|
view.onLongClick(336.0, 37.0)
|
||||||
verify(dateClickedListener).onDateLongPress(LocalDate(2015, 1, 25))
|
verify(dateClickedListener).onDateLongPress(
|
||||||
|
ScreenLocation(336.0, 37.0),
|
||||||
|
LocalDate(2015, 1, 25)
|
||||||
|
)
|
||||||
reset(dateClickedListener)
|
reset(dateClickedListener)
|
||||||
|
|
||||||
// Click header
|
// Click header
|
||||||
|
|||||||
Reference in New Issue
Block a user