Compare commits

...

11 Commits

23 changed files with 224 additions and 305 deletions

View File

@@ -17,7 +17,7 @@ jobs:
run: ./build.sh build
- name: Run Android tests
run: ./build.sh android-tests-parallel 23 24 25 26 27 28 30 31
run: ./build.sh android-tests-parallel 23 24 25 26 28 30 31
- name: Upload artifacts
if: always()

View File

@@ -217,20 +217,28 @@ android_test_parallel() {
(
LOG=build/android-test-$API.log
log_info "API $API: Running tests..."
if android_test $API 1>$LOG 2>&1; then
android_test $API 1>$LOG 2>&1
ret_code=$?
if [ $ret_code = 0 ]; then
log_info "API $API: Passed"
else
log_error "API $API: Failed"
fi
pkill -9 -f ${AVD_PREFIX}${API}
exit $ret_code
)&
PIDS+=" $!"
done
# Check exit codes
RET_CODE=0
success=0
for pid in $PIDS; do
wait $pid || RET_CODE=1
wait $pid
ret_code=$?
if [ $ret_code != 0 ]; then
success=1
fi
echo pid=$pid ret_code=$ret_code success=$success
done
# Print all logs
@@ -240,7 +248,7 @@ android_test_parallel() {
echo "::endgroup::"
done
return $RET_CODE
return $success
}
android_build() {

View File

@@ -35,8 +35,8 @@ android {
compileSdk = 31
defaultConfig {
versionCode = 20101
versionName = "2.1.1"
versionCode = 20102
versionName = "2.1.2"
minSdk = 23
targetSdk = 31
applicationId = "org.isoron.uhabits"

View File

@@ -44,7 +44,7 @@ class EntryButtonViewTest : BaseViewTest() {
view = component.getEntryButtonViewFactory().create().apply {
value = Entry.NO
color = PaletteUtils.getAndroidTestColor(5)
onToggle = { _, _, _ -> toggled = true }
onToggle = { _, _ -> toggled = true }
onEdit = { edited = true }
}
measureView(view, dpToPixels(48), dpToPixels(48))

View File

@@ -77,7 +77,7 @@ class EntryPanelViewTest : BaseViewTest() {
@Test
fun testToggle() {
val timestamps = mutableListOf<Timestamp>()
view.onToggle = { t, _, _, _ -> timestamps.add(t) }
view.onToggle = { t, _, _ -> timestamps.add(t) }
view.buttons[0].performLongClick()
view.buttons[2].performLongClick()
view.buttons[3].performLongClick()
@@ -88,7 +88,7 @@ class EntryPanelViewTest : BaseViewTest() {
fun testToggle_withOffset() {
val timestamps = mutableListOf<Timestamp>()
view.dataOffset = 3
view.onToggle = { t, _, _, _ -> timestamps += t }
view.onToggle = { t, _, _ -> timestamps += t }
view.buttons[0].performLongClick()
view.buttons[2].performLongClick()
view.buttons[3].performLongClick()

View File

@@ -20,109 +20,60 @@
package org.isoron.uhabits.activities.common.dialogs
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.appcompat.app.AppCompatDialogFragment
import org.isoron.uhabits.HabitsApplication
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.dismissCurrentAndShow
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,
) {
class CheckmarkDialog : AppCompatDialogFragment() {
var onToggle: (Int, String) -> Unit = { _, _ -> }
private lateinit var dialog: Dialog
private val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)).apply {
// Required for round corners
container.clipToOutline = true
}
init {
view.booleanButtons.visibility = VISIBLE
initColors()
initTypefaces()
hideDisabledButtons()
populate()
}
private fun initColors() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val appComponent = (requireActivity().application as HabitsApplication).component
val prefs = appComponent.preferences
val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context))
arrayOf(view.yesBtn, view.skipBtn).forEach {
it.setTextColor(color)
it.setTextColor(requireArguments().getInt("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)
it.typeface = getFontAwesome(requireContext())
}
}
private fun hideDisabledButtons() {
view.notes.setText(requireArguments().getString("notes")!!)
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
}
view.notes.setText(notes)
}
fun show() {
dialog = Dialog(context, android.R.style.Theme_NoTitleBar)
view.booleanButtons.visibility = VISIBLE
val dialog = Dialog(requireContext())
dialog.setContentView(view.root)
dialog.window?.apply {
setLayout(
view.root.dp(POPUP_WIDTH).toInt(),
view.root.dp(POPUP_HEIGHT).toInt()
)
setBackgroundDrawableResource(android.R.color.transparent)
}
fun onClick(v: Int) {
this.value = v
save()
val notes = view.notes.text.toString().trim()
onToggle(v, notes)
requireDialog().dismiss()
}
view.yesBtn.setOnClickListener { onClick(YES_MANUAL) }
view.noBtn.setOnClickListener { onClick(NO) }
view.skipBtn.setOnClickListener { onClick(SKIP) }
view.unknownBtn.setOnClickListener { onClick(UNKNOWN) }
dialog.setCanceledOnTouchOutside(true)
dialog.dimBehind()
dialog.dismissCurrentAndShow()
view.notes.setOnEditorActionListener { v, actionId, event ->
onClick(requireArguments().getInt("value"))
true
}
fun save() {
onToggle(value, view.notes.text.toString().trim())
dialog.dismiss()
return dialog
}
}

View File

@@ -0,0 +1,104 @@
package org.isoron.uhabits.activities.common.dialogs
import android.app.Dialog
import android.os.Bundle
import android.text.method.DigitsKeyListener
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import androidx.appcompat.app.AppCompatDialogFragment
import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
import org.isoron.uhabits.utils.InterfaceUtils
import org.isoron.uhabits.utils.requestFocusWithKeyboard
import org.isoron.uhabits.utils.sres
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.text.NumberFormat
import java.text.ParseException
class NumberDialog : AppCompatDialogFragment() {
var onToggle: (Double, String) -> Unit = { _, _ -> }
var onDismiss: () -> Unit = {}
private var originalNotes: String = ""
private var originalValue: Double = 0.0
private lateinit var view: CheckmarkPopupBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val appComponent = (requireActivity().application as HabitsApplication).component
val prefs = appComponent.preferences
view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context))
arrayOf(view.yesBtn, view.skipBtn).forEach {
it.setTextColor(requireArguments().getInt("color"))
}
arrayOf(view.noBtn, view.unknownBtn).forEach {
it.setTextColor(view.root.sres.getColor(R.attr.contrast60))
}
arrayOf(view.yesBtn, view.noBtn, view.skipBtn, view.unknownBtn).forEach {
it.typeface = InterfaceUtils.getFontAwesome(requireContext())
}
if (!prefs.isSkipEnabled) view.skipBtnNumber.visibility = View.GONE
view.numberButtons.visibility = View.VISIBLE
fixDecimalSeparator(view)
originalNotes = requireArguments().getString("notes")!!
originalValue = requireArguments().getDouble("value")
view.notes.setText(originalNotes)
view.value.setText(
when {
originalValue < 0.01 -> "0"
else -> DecimalFormat("#.##").format(originalValue)
}
)
view.value.setOnKeyListener { _, keyCode, event ->
if (event.action == MotionEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
save()
return@setOnKeyListener true
}
return@setOnKeyListener false
}
view.saveBtn.setOnClickListener {
save()
}
view.skipBtnNumber.setOnClickListener {
view.value.setText((Entry.SKIP.toDouble() / 1000).toString())
save()
}
view.notes.setOnEditorActionListener { v, actionId, event ->
save()
true
}
view.value.requestFocusWithKeyboard()
val dialog = Dialog(requireContext())
dialog.setContentView(view.root)
dialog.window?.apply {
setBackgroundDrawableResource(android.R.color.transparent)
}
dialog.setOnDismissListener { onDismiss() }
return dialog
}
private fun fixDecimalSeparator(view: CheckmarkPopupBinding) {
// https://stackoverflow.com/a/34256139
val separator = DecimalFormatSymbols.getInstance().decimalSeparator
view.value.keyListener = DigitsKeyListener.getInstance("0123456789$separator")
}
fun save() {
var value = originalValue
try {
val numberFormat = NumberFormat.getInstance()
val valueStr = view.value.text.toString()
value = numberFormat.parse(valueStr)!!.toDouble()
} catch (e: ParseException) {
// NOP
}
val notes = view.notes.text.toString()
onToggle(value, notes)
requireDialog().dismiss()
}
}

View File

@@ -1,116 +0,0 @@
/*
* 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.app.Dialog
import android.content.Context
import android.view.KeyEvent.KEYCODE_ENTER
import android.view.LayoutInflater
import android.view.MotionEvent.ACTION_DOWN
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
import org.isoron.uhabits.utils.dimBehind
import org.isoron.uhabits.utils.dismissCurrentAndShow
import org.isoron.uhabits.utils.dp
import org.isoron.uhabits.utils.requestFocusWithKeyboard
import java.text.DecimalFormat
class NumberPopup(
private val context: Context,
private var notes: String,
private var value: Double,
private val prefs: Preferences,
private val anchor: View,
) {
var onToggle: (Double, String) -> Unit = { _, _ -> }
var onDismiss: () -> Unit = {}
private val originalValue = value
private lateinit var dialog: Dialog
private val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)).apply {
// Required for round corners
container.clipToOutline = true
}
init {
view.numberButtons.visibility = VISIBLE
hideDisabledButtons()
populate()
}
private fun hideDisabledButtons() {
if (!prefs.isSkipEnabled) view.skipBtnNumber.visibility = GONE
}
private fun populate() {
view.notes.setText(notes)
view.value.setText(
when {
value < 0.01 -> "0"
else -> DecimalFormat("#.##").format(value)
}
)
}
fun show() {
dialog = Dialog(context, android.R.style.Theme_NoTitleBar)
dialog.setContentView(view.root)
dialog.window?.apply {
setLayout(
view.root.dp(POPUP_WIDTH).toInt(),
view.root.dp(POPUP_HEIGHT).toInt()
)
setBackgroundDrawableResource(android.R.color.transparent)
}
dialog.setOnDismissListener {
onDismiss()
}
view.value.setOnKeyListener { _, keyCode, event ->
if (event.action == ACTION_DOWN && keyCode == KEYCODE_ENTER) {
save()
return@setOnKeyListener true
}
return@setOnKeyListener false
}
view.saveBtn.setOnClickListener {
save()
}
view.skipBtnNumber.setOnClickListener {
view.value.setText((Entry.SKIP.toDouble() / 1000).toString())
save()
}
view.value.requestFocusWithKeyboard()
dialog.setCanceledOnTouchOutside(true)
dialog.dimBehind()
dialog.dismissCurrentAndShow()
}
fun save() {
val value = view.value.text.toString().toDoubleOrNull() ?: originalValue
val notes = view.notes.text.toString()
onToggle(value, notes)
dialog.dismiss()
}
}

View File

@@ -40,6 +40,7 @@ import org.isoron.uhabits.inject.ActivityContextModule
import org.isoron.uhabits.inject.DaggerHabitsActivityComponent
import org.isoron.uhabits.inject.HabitsActivityComponent
import org.isoron.uhabits.inject.HabitsApplicationComponent
import org.isoron.uhabits.utils.dismissCurrentDialog
import org.isoron.uhabits.utils.restartWithFade
class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
@@ -91,6 +92,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
midnightTimer.onPause()
screen.onDetached()
adapter.cancelRefresh()
dismissCurrentDialog()
super.onPause()
}

View File

@@ -22,14 +22,15 @@ package org.isoron.uhabits.activities.habits.list
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import dagger.Lazy
import org.isoron.platform.gui.toInt
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.common.dialogs.CheckmarkPopup
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
import org.isoron.uhabits.activities.common.dialogs.NumberPopup
import org.isoron.uhabits.activities.common.dialogs.NumberDialog
import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
@@ -233,17 +234,14 @@ class ListHabitsScreen
notes: String,
callback: ListHabitsBehavior.NumberPickerCallback
) {
val view = rootView.get()
NumberPopup(
context = context,
prefs = preferences,
anchor = view,
notes = notes,
value = value,
).apply {
onToggle = { value, notes -> callback.onNumberPicked(value, notes) }
show()
val fm = (context as AppCompatActivity).supportFragmentManager
val dialog = NumberDialog()
dialog.arguments = Bundle().apply {
putDouble("value", value)
putString("notes", notes)
}
dialog.onToggle = { v, n -> callback.onNumberPicked(v, n) }
dialog.dismissCurrentAndShow(fm, "numberDialog")
}
override fun showCheckmarkPopup(
@@ -252,18 +250,16 @@ class ListHabitsScreen
color: PaletteColor,
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()
val theme = rootView.get().currentTheme()
val fm = (context as AppCompatActivity).supportFragmentManager
val dialog = CheckmarkDialog()
dialog.arguments = Bundle().apply {
putInt("color", theme.color(color).toInt())
putInt("value", selectedValue)
putString("notes", notes)
}
dialog.onToggle = { v, n -> callback.onNotesSaved(v, n) }
dialog.dismissCurrentAndShow(fm, "checkmarkDialog")
}
private fun getExecuteString(command: Command): String? {

View File

@@ -44,8 +44,6 @@ import org.isoron.uhabits.utils.sres
import org.isoron.uhabits.utils.toMeasureSpec
import javax.inject.Inject
const val TOGGLE_DELAY_MILLIS = 2000L
class CheckmarkButtonViewFactory
@Inject constructor(
@ActivityContext val context: Context,
@@ -79,7 +77,7 @@ class CheckmarkButtonView(
invalidate()
}
var onToggle: (Int, String, Long) -> Unit = { _, _, _ -> }
var onToggle: (Int, String) -> Unit = { _, _ -> }
var onEdit: () -> Unit = { }
@@ -90,25 +88,25 @@ class CheckmarkButtonView(
setOnLongClickListener(this)
}
fun performToggle(delay: Long) {
fun performToggle() {
value = Entry.nextToggleValue(
value = value,
isSkipEnabled = preferences.isSkipEnabled,
areQuestionMarksEnabled = preferences.areQuestionMarksEnabled
)
onToggle(value, notes, delay)
onToggle(value, notes)
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
invalidate()
}
override fun onClick(v: View) {
if (preferences.isShortToggleEnabled) performToggle(TOGGLE_DELAY_MILLIS)
if (preferences.isShortToggleEnabled) performToggle()
else onEdit()
}
override fun onLongClick(v: View): Boolean {
if (preferences.isShortToggleEnabled) onEdit()
else performToggle(TOGGLE_DELAY_MILLIS)
else performToggle()
return true
}

View File

@@ -60,7 +60,7 @@ class CheckmarkPanelView(
setupButtons()
}
var onToggle: (Timestamp, Int, String, Long) -> Unit = { _, _, _, _ -> }
var onToggle: (Timestamp, Int, String) -> Unit = { _, _, _ -> }
set(value) {
field = value
setupButtons()
@@ -89,7 +89,7 @@ class CheckmarkPanelView(
else -> ""
}
button.color = color
button.onToggle = { value, notes, delay -> onToggle(timestamp, value, notes, delay) }
button.onToggle = { value, notes -> onToggle(timestamp, value, notes) }
button.onEdit = { onEdit(timestamp) }
}
}

View File

@@ -57,13 +57,6 @@ class HabitCardViewFactory
fun create() = HabitCardView(context, checkmarkPanelFactory, numberPanelFactory, behavior)
}
data class DelayedToggle(
var habit: Habit,
var timestamp: Timestamp,
var value: Int,
var notes: String
)
class HabitCardView(
@ActivityContext context: Context,
checkmarkPanelFactory: CheckmarkPanelViewFactory,
@@ -136,7 +129,6 @@ class HabitCardView(
private var scoreRing: RingView
private var currentToggleTaskId = 0
private var queuedToggles = mutableListOf<DelayedToggle>()
init {
scoreRing = RingView(context).apply {
@@ -160,12 +152,9 @@ class HabitCardView(
}
checkmarkPanel = checkmarkPanelFactory.create().apply {
onToggle = { timestamp, value, notes, delay ->
if (delay > 0) triggerRipple(timestamp)
habit?.let {
val taskId = queueToggle(it, timestamp, value, notes);
{ runPendingToggles(taskId) }.delay(delay)
}
onToggle = { timestamp, value, notes ->
triggerRipple(timestamp)
habit?.let { behavior.onToggle(it, timestamp, value, notes) }
}
onEdit = { timestamp ->
triggerRipple(timestamp)
@@ -205,25 +194,6 @@ class HabitCardView(
addView(innerFrame)
}
@Synchronized
private fun runPendingToggles(id: Int) {
if (currentToggleTaskId != id) return
for ((h, t, v, n) in queuedToggles) behavior.onToggle(h, t, v, n)
queuedToggles.clear()
}
@Synchronized
private fun queueToggle(
it: Habit,
timestamp: Timestamp,
value: Int,
notes: String,
): Int {
currentToggleTaskId += 1
queuedToggles.add(DelayedToggle(it, timestamp, value, notes))
return currentToggleTaskId
}
override fun onModelChange() {
Handler(Looper.getMainLooper()).post {
habit?.let { copyAttributesFrom(it) }

View File

@@ -34,10 +34,10 @@ import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.AndroidThemeSwitcher
import org.isoron.uhabits.activities.HabitsDirFinder
import org.isoron.uhabits.activities.common.dialogs.CheckmarkPopup
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog
import org.isoron.uhabits.activities.common.dialogs.NumberPopup
import org.isoron.uhabits.activities.common.dialogs.NumberDialog
import org.isoron.uhabits.core.commands.Command
import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.models.Habit
@@ -51,6 +51,7 @@ import org.isoron.uhabits.core.ui.views.OnDateClickedListener
import org.isoron.uhabits.intents.IntentFactory
import org.isoron.uhabits.utils.currentTheme
import org.isoron.uhabits.utils.dismissCurrentAndShow
import org.isoron.uhabits.utils.dismissCurrentDialog
import org.isoron.uhabits.utils.showMessage
import org.isoron.uhabits.utils.showSendFileScreen
import org.isoron.uhabits.widgets.WidgetUpdater
@@ -129,6 +130,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
}
override fun onPause() {
dismissCurrentDialog()
commandRunner.removeListener(this)
super.onPause()
}
@@ -170,41 +172,32 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
override fun showNumberPopup(
value: Double,
notes: String,
preferences: Preferences,
callback: ListHabitsBehavior.NumberPickerCallback
) {
val anchor = getPopupAnchor() ?: return
NumberPopup(
context = this@ShowHabitActivity,
prefs = preferences,
notes = notes,
anchor = anchor,
value = value,
).apply {
onToggle = { v, n -> callback.onNumberPicked(v, n) }
show()
val dialog = NumberDialog()
dialog.arguments = Bundle().apply {
putDouble("value", value)
putString("notes", notes)
}
dialog.onToggle = { v, n -> callback.onNumberPicked(v, n) }
dialog.dismissCurrentAndShow(supportFragmentManager, "numberDialog")
}
override fun showCheckmarkPopup(
selectedValue: Int,
notes: String,
preferences: Preferences,
color: PaletteColor,
callback: ListHabitsBehavior.CheckMarkDialogCallback
) {
val anchor = getPopupAnchor() ?: return
CheckmarkPopup(
context = this@ShowHabitActivity,
prefs = preferences,
notes = notes,
color = view.currentTheme().color(color).toInt(),
anchor = anchor,
value = selectedValue,
).apply {
onToggle = { v, n -> callback.onNotesSaved(v, n) }
show()
val theme = view.currentTheme()
val dialog = CheckmarkDialog()
dialog.arguments = Bundle().apply {
putInt("color", theme.color(color).toInt())
putInt("value", selectedValue)
putString("notes", notes)
}
dialog.onToggle = { v, n -> callback.onNotesSaved(v, n) }
dialog.dismissCurrentAndShow(supportFragmentManager, "checkmarkDialog")
}
private fun getPopupAnchor(): View? {

View File

@@ -6,16 +6,24 @@ import androidx.fragment.app.FragmentManager
import java.lang.ref.WeakReference
var currentDialog: WeakReference<Dialog> = WeakReference(null)
var currentDialogFragment: WeakReference<DialogFragment> = WeakReference(null)
fun dismissCurrentDialog() {
currentDialog.get()?.dismiss()
currentDialog = WeakReference(null)
currentDialogFragment.get()?.dismiss()
currentDialogFragment = WeakReference(null)
}
fun Dialog.dismissCurrentAndShow() {
currentDialog.get()?.dismiss()
dismissCurrentDialog()
currentDialog = WeakReference(this)
show()
}
fun DialogFragment.dismissCurrentAndShow(fragmentManager: FragmentManager, tag: String) {
currentDialog.get()?.dismiss()
dismissCurrentDialog()
currentDialogFragment = WeakReference(this)
show(fragmentManager, tag)
fragmentManager.executePendingTransactions()
currentDialog = WeakReference(this.dialog)
}

View File

@@ -43,7 +43,7 @@ open class CheckmarkWidget(
override fun getOnClickPendingIntent(context: Context): PendingIntent? {
return if (habit.isNumerical) {
pendingIntentFactory.showNumberPicker(habit, DateUtils.getToday())
pendingIntentFactory.showNumberPicker(habit, DateUtils.getTodayWithOffset())
} else {
pendingIntentFactory.toggleCheckmark(habit, null)
}

View File

@@ -33,7 +33,7 @@ import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitNotFoundException
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
import org.isoron.uhabits.intents.IntentFactory
import org.isoron.uhabits.intents.PendingIntentFactory
import org.isoron.uhabits.utils.InterfaceUtils.dpToPixels
@@ -101,7 +101,7 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
val landscapeViews = widget.landscapeRemoteViews
val portraitViews = widget.portraitRemoteViews
val factory = PendingIntentFactory(context, IntentFactory())
val intent = StackWidgetType.getIntentFillIn(factory, widgetType, h, habits, getToday())
val intent = StackWidgetType.getIntentFillIn(factory, widgetType, h, habits, getTodayWithOffset())
landscapeViews.setOnClickFillInIntent(R.id.button, intent)
portraitViews.setOnClickFillInIntent(R.id.button, intent)
val remoteViews = RemoteViews(landscapeViews, portraitViews)

View File

@@ -21,8 +21,10 @@
<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"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="128dp"
android:minWidth="208dp"
app:divider="@drawable/checkmark_dialog_divider"
app:showDividers="middle"
android:orientation="vertical"
@@ -34,7 +36,7 @@
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:inputType="textCapSentences|textMultiLine"
android:inputType="textCapSentences"
android:textSize="@dimen/smallTextSize"
android:padding="4dp"
android:background="@color/transparent"

View File

@@ -149,7 +149,7 @@ class HabitsCSVExporter(
val timeframe = getTimeframe()
val oldest = timeframe[0]
val newest = DateUtils.getToday()
val newest = DateUtils.getTodayWithOffset()
val checkmarks: MutableList<ArrayList<Entry>> = ArrayList()
val scores: MutableList<ArrayList<Score>> = ArrayList()
for (habit in selectedHabits) {

View File

@@ -204,9 +204,16 @@ open class EntryList {
// Copy original entries
original.forEach { entry ->
val offset = entry.timestamp.daysUntil(to)
if (result[offset].value == UNKNOWN || entry.value == SKIP || entry.value == YES_MANUAL) {
result[offset] = entry
val value = if (
result[offset].value == UNKNOWN ||
entry.value == SKIP ||
entry.value == YES_MANUAL
) {
entry.value
} else {
YES_AUTO
}
result[offset] = Entry(entry.timestamp, value, entry.notes)
}
return result

View File

@@ -57,7 +57,7 @@ class BarCardPresenter(
} else {
boolBucketSizes[boolSpinnerPosition]
}
val today = DateUtils.getToday()
val today = DateUtils.getTodayWithOffset()
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
val entries = habit.computedEntries.getByInterval(oldest, today).groupedSum(
truncateField = ScoreCardPresenter.getTruncateField(bucketSize),

View File

@@ -91,7 +91,6 @@ class HistoryCardPresenter(
screen.showCheckmarkPopup(
entry.value,
entry.notes,
preferences,
habit.color,
) { newValue, newNotes ->
commandRunner.run(
@@ -130,7 +129,6 @@ class HistoryCardPresenter(
screen.showNumberPopup(
value = oldValue / 1000.0,
notes = entry.notes,
preferences = preferences,
) { newValue: Double, newNotes: String ->
val thousands = (newValue * 1000).roundToInt()
commandRunner.run(
@@ -203,13 +201,11 @@ class HistoryCardPresenter(
fun showNumberPopup(
value: Double,
notes: String,
preferences: Preferences,
callback: ListHabitsBehavior.NumberPickerCallback,
)
fun showCheckmarkPopup(
selectedValue: Int,
notes: String,
preferences: Preferences,
color: PaletteColor,
callback: ListHabitsBehavior.CheckMarkDialogCallback,
)

View File

@@ -217,7 +217,7 @@ class EntryListTest {
fun testAddFromInterval() {
val entries = listOf(
Entry(day(1), YES_MANUAL),
Entry(day(2), NO),
Entry(day(2), NO, "Test"),
Entry(day(4), NO),
Entry(day(5), YES_MANUAL),
Entry(day(10), YES_MANUAL),
@@ -230,7 +230,7 @@ class EntryListTest {
)
val expected = listOf(
Entry(day(1), YES_MANUAL),
Entry(day(2), YES_AUTO),
Entry(day(2), YES_AUTO, "Test"),
Entry(day(3), UNKNOWN),
Entry(day(4), YES_AUTO),
Entry(day(5), YES_MANUAL),