Improve automatic checkmarks for monthly habits

Fixes #947
This commit is contained in:
2021-06-05 16:54:13 -05:00
parent 2163a2b93b
commit 33468bfc1c
7 changed files with 62 additions and 61 deletions

View File

@@ -1,5 +1,9 @@
# Changelog # Changelog
## [2.0.3] - [Unreleased]
### Fixed
- Improve automatic checkmarks for monthly habits (@iSoron, 947)
## [2.0.2] - 2021-05-23 ## [2.0.2] - 2021-05-23
### Changed ### Changed

View File

@@ -37,8 +37,8 @@ android {
compileSdkVersion(30) compileSdkVersion(30)
defaultConfig { defaultConfig {
versionCode(20002) versionCode(20003)
versionName("2.0.2") versionName("2.0.3")
minSdkVersion(23) minSdkVersion(23)
targetSdkVersion(30) targetSdkVersion(30)
applicationId("org.isoron.uhabits") applicationId("org.isoron.uhabits")

View File

@@ -160,26 +160,28 @@ class FrequencyPickerDialog(
private fun populateViews() { private fun populateViews() {
uncheckAll() uncheckAll()
if (freqNumerator == 1) { if (freqDenominator == 30 || freqDenominator == 31) {
if (freqDenominator == 1) { contentView.xTimesPerMonthRadioButton.isChecked = true
contentView.everyDayRadioButton.isChecked = true contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
} else { focus(contentView.xTimesPerMonthTextView)
contentView.everyXDaysRadioButton.isChecked = true
contentView.everyXDaysTextView.setText(freqDenominator.toString())
focus(contentView.everyXDaysTextView)
}
} else { } else {
if (freqDenominator == 7) { if (freqNumerator == 1) {
contentView.xTimesPerWeekRadioButton.isChecked = true if (freqDenominator == 1) {
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString()) contentView.everyDayRadioButton.isChecked = true
focus(contentView.xTimesPerWeekTextView) } else {
} else if (freqDenominator == 30 || freqDenominator == 31) { contentView.everyXDaysRadioButton.isChecked = true
contentView.xTimesPerMonthRadioButton.isChecked = true contentView.everyXDaysTextView.setText(freqDenominator.toString())
contentView.xTimesPerMonthTextView.setText(freqNumerator.toString()) focus(contentView.everyXDaysTextView)
focus(contentView.xTimesPerMonthTextView) }
} else { } else {
Log.w("FrequencyPickerDialog", "Unknown frequency: $freqNumerator/$freqDenominator") if (freqDenominator == 7) {
contentView.everyDayRadioButton.isChecked = true contentView.xTimesPerWeekRadioButton.isChecked = true
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
focus(contentView.xTimesPerWeekTextView)
} else {
Log.w("FrequencyPickerDialog", "Unknown frequency: $freqNumerator/$freqDenominator")
contentView.everyDayRadioButton.isChecked = true
}
} }
} }
} }

View File

@@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.edit
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.Html import android.text.Html
@@ -59,6 +60,16 @@ import org.isoron.uhabits.utils.formatTime
import org.isoron.uhabits.utils.toFormattedString import org.isoron.uhabits.utils.toFormattedString
import org.isoron.uhabits.utils.toThemedAndroidColor import org.isoron.uhabits.utils.toThemedAndroidColor
fun formatFrequency(freqNum: Int, freqDen: Int, resources: Resources) = when {
freqNum == 1 && (freqDen == 30 || freqDen == 31) -> resources.getString(R.string.every_month)
freqDen == 30 || freqDen == 31 -> resources.getString(R.string.x_times_per_month, freqNum)
freqNum == 1 && freqDen == 1 -> resources.getString(R.string.every_day)
freqNum == 1 && freqDen == 7 -> resources.getString(R.string.every_week)
freqNum == 1 && freqDen > 1 -> resources.getString(R.string.every_x_days, freqDen)
freqDen == 7 -> resources.getString(R.string.x_times_per_week, freqNum)
else -> "$freqNum/$freqDen"
}
class EditHabitActivity : AppCompatActivity() { class EditHabitActivity : AppCompatActivity() {
private lateinit var themeSwitcher: AndroidThemeSwitcher private lateinit var themeSwitcher: AndroidThemeSwitcher
@@ -299,14 +310,7 @@ class EditHabitActivity : AppCompatActivity() {
@SuppressLint("StringFormatMatches") @SuppressLint("StringFormatMatches")
private fun populateFrequency() { private fun populateFrequency() {
binding.booleanFrequencyPicker.text = when { binding.booleanFrequencyPicker.text = formatFrequency(freqNum, freqDen, resources)
freqNum == 1 && freqDen == 1 -> getString(R.string.every_day)
freqNum == 1 && freqDen == 7 -> getString(R.string.every_week)
freqNum == 1 && freqDen > 1 -> getString(R.string.every_x_days, freqDen)
freqDen == 7 -> getString(R.string.x_times_per_week, freqNum)
freqDen == 30 || freqDen == 31 -> getString(R.string.x_times_per_month, freqNum)
else -> "$freqNum/$freqDen"
}
binding.numericalFrequencyPicker.text = when (freqDen) { binding.numericalFrequencyPicker.text = when (freqDen) {
1 -> getString(R.string.every_day) 1 -> getString(R.string.every_day)
7 -> getString(R.string.every_week) 7 -> getString(R.string.every_week)

View File

@@ -20,14 +20,13 @@ package org.isoron.uhabits.activities.habits.show.views
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.Resources
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import org.isoron.uhabits.R import org.isoron.uhabits.R
import org.isoron.uhabits.activities.habits.edit.formatFrequency
import org.isoron.uhabits.activities.habits.list.views.toShortString import org.isoron.uhabits.activities.habits.list.views.toShortString
import org.isoron.uhabits.core.models.Frequency
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
import org.isoron.uhabits.utils.InterfaceUtils import org.isoron.uhabits.utils.InterfaceUtils
@@ -49,7 +48,11 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
fun setState(state: SubtitleCardState) { fun setState(state: SubtitleCardState) {
val color = state.color.toThemedAndroidColor(context) val color = state.color.toThemedAndroidColor(context)
val reminder = state.reminder val reminder = state.reminder
binding.frequencyLabel.text = state.frequency.format(resources) binding.frequencyLabel.text = formatFrequency(
state.frequency.numerator,
state.frequency.denominator,
resources,
)
binding.questionLabel.setTextColor(color) binding.questionLabel.setTextColor(color)
binding.questionLabel.text = state.question binding.questionLabel.text = state.question
binding.reminderLabel.text = if (reminder != null) { binding.reminderLabel.text = if (reminder != null) {
@@ -72,32 +75,4 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
postInvalidate() postInvalidate()
} }
@SuppressLint("StringFormatMatches")
private fun Frequency.format(resources: Resources): String {
val num = this.numerator
val den = this.denominator
if (num == den) {
return resources.getString(R.string.every_day)
}
if (den == 7) {
return resources.getString(R.string.x_times_per_week, num)
}
if (den == 30 || den == 31) {
return resources.getString(R.string.x_times_per_month, num)
}
if (num == 1) {
if (den == 7) {
return resources.getString(R.string.every_week)
}
if (den % 7 == 0) {
return resources.getString(R.string.every_x_weeks, den / 7)
}
if (den == 30 || den == 31) {
return resources.getString(R.string.every_month)
}
return resources.getString(R.string.every_x_days, den)
}
return "$num/$den"
}
} }

View File

@@ -72,6 +72,13 @@ data class LocalDate(val daysSince2000: Int) {
return dayCache return dayCache
} }
val monthLength: Int
get() = when (month) {
4, 6, 9, 11 -> 30
2 -> if (isLeapYear(year)) 29 else 28
else -> 31
}
private fun updateYearMonthDayCache() { private fun updateYearMonthDayCache() {
var currYear = 2000 var currYear = 2000
var currDay = 0 var currDay = 0

View File

@@ -248,8 +248,17 @@ open class EntryList {
for (i in num - 1 until filtered.size) { for (i in num - 1 until filtered.size) {
val (begin, _) = filtered[i] val (begin, _) = filtered[i]
val (center, _) = filtered[i - num + 1] val (center, _) = filtered[i - num + 1]
if (begin.daysUntil(center) < den) { var size = den
val end = begin.plus(den - 1) if (den == 30 || den == 31) {
val beginDate = begin.toLocalDate()
size = if (beginDate.day == beginDate.monthLength) {
beginDate.plus(1).monthLength
} else {
beginDate.monthLength
}
}
if (begin.daysUntil(center) < size) {
val end = begin.plus(size - 1)
intervals.add(Interval(begin, center, end)) intervals.add(Interval(begin, center, end))
} }
} }