diff --git a/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/render.png b/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/render.png index 3d7861df0..5bdcbc1e1 100644 Binary files a/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/render.png and b/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/render.png differ diff --git a/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/renderDifferentSize.png b/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/renderDifferentSize.png index 0fefe39b0..07dd3da6a 100644 Binary files a/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/renderDifferentSize.png and b/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/renderDifferentSize.png differ diff --git a/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/renderTransparent.png b/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/renderTransparent.png index 3d7861df0..5bdcbc1e1 100644 Binary files a/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/renderTransparent.png and b/uhabits-android/src/androidTest/assets/views/common/FrequencyChart/renderTransparent.png differ diff --git a/uhabits-android/src/androidTest/assets/views/habits/show/FrequencyCard/render.png b/uhabits-android/src/androidTest/assets/views/habits/show/FrequencyCard/render.png index 969c7ff28..4e6d23048 100644 Binary files a/uhabits-android/src/androidTest/assets/views/habits/show/FrequencyCard/render.png and b/uhabits-android/src/androidTest/assets/views/habits/show/FrequencyCard/render.png differ diff --git a/uhabits-android/src/androidTest/assets/views/widgets/FrequencyWidget/render.png b/uhabits-android/src/androidTest/assets/views/widgets/FrequencyWidget/render.png index 452e0dd68..3fb786226 100644 Binary files a/uhabits-android/src/androidTest/assets/views/widgets/FrequencyWidget/render.png and b/uhabits-android/src/androidTest/assets/views/widgets/FrequencyWidget/render.png differ diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt index 93a97dc41..417ab21f2 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.kt @@ -29,6 +29,7 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.getShortWeekdayNames import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendar import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendarWithOffset import org.isoron.uhabits.core.utils.DateUtils.Companion.getWeekdaySequence +import org.isoron.uhabits.core.utils.DateUtils.Companion.getWeekdaysInMonth import org.isoron.uhabits.utils.ColorUtils.mixColors import org.isoron.uhabits.utils.StyledResources import org.isoron.uhabits.utils.toSimpleDataFormat @@ -62,7 +63,6 @@ class FrequencyChart : ScrollableChart { private var primaryColor = 0 private var isBackgroundTransparent = false private lateinit var frequency: HashMap> - private var maxFreq = 0 private var firstWeekday = Calendar.SUNDAY constructor(context: Context?) : super(context) { @@ -82,7 +82,6 @@ class FrequencyChart : ScrollableChart { fun setFrequency(frequency: java.util.HashMap>) { this.frequency = frequency - maxFreq = getMaxFreq(frequency) postInvalidate() } @@ -91,15 +90,6 @@ class FrequencyChart : ScrollableChart { postInvalidate() } - private fun getMaxFreq(frequency: HashMap>): Int { - var maxValue = 1 - for (values in frequency.values) for (value in values) maxValue = max( - value, - maxValue - ) - return maxValue - } - fun setIsBackgroundTransparent(isBackgroundTransparent: Boolean) { this.isBackgroundTransparent = isBackgroundTransparent initColors() @@ -166,6 +156,7 @@ class FrequencyChart : ScrollableChart { private fun drawColumn(canvas: Canvas, rect: RectF?, date: GregorianCalendar) { val values = frequency[Timestamp(date)] + val weekDaysInMonth = getWeekdaysInMonth(Timestamp(date)) val rowHeight = rect!!.height() / 8.0f prevRect!!.set(rect) val localeWeekdayList: Array = getWeekdaySequence(firstWeekday) @@ -174,7 +165,7 @@ class FrequencyChart : ScrollableChart { rect.offset(prevRect!!.left, prevRect!!.top + baseSize * j) val i = localeWeekdayList[j] % 7 if (values != null) - drawMarker(canvas, rect, values[i]) + drawMarker(canvas, rect, values[i], weekDaysInMonth[i]) rect.offset(0f, rowHeight) } drawFooter(canvas, rect, date) @@ -222,7 +213,7 @@ class FrequencyChart : ScrollableChart { canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid!!) } - private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?) { + private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?, frequency: Int) { // value can be negative when the entry is skipped val valueCopy = value?.let { max(0, it) } @@ -230,7 +221,8 @@ class FrequencyChart : ScrollableChart { // maximal allowed mark radius val maxRadius = (rect.height() - 2 * padding) / 2.0f // the real mark radius is scaled down by a factor depending on the maximal frequency - val scale = 1.0f / maxFreq * valueCopy!! + + val scale = 1.0f / frequency * valueCopy!! val radius = maxRadius * scale val colorIndex = min((colors.size - 1), ((colors.size - 1) * scale).roundToInt()) pGraph!!.color = colors[colorIndex] @@ -293,6 +285,5 @@ class FrequencyChart : ScrollableChart { frequency[Timestamp(date)] = values date.add(Calendar.MONTH, -1) } - maxFreq = getMaxFreq(frequency) } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt index 7c2c0a4fd..b06513140 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt @@ -19,6 +19,7 @@ package org.isoron.uhabits.core.utils import org.isoron.uhabits.core.models.Timestamp +import java.time.YearMonth import java.util.Calendar import java.util.Calendar.DAY_OF_MONTH import java.util.Calendar.DAY_OF_WEEK @@ -178,6 +179,26 @@ abstract class DateUtils { return getWeekdayNames(GregorianCalendar.SHORT, firstWeekday) } + /** + * Returns a vector of Int representing the frequency of each weekday in a given month. + * + * @param startOfMonth a Timestamp representing the beginning of the month. + */ + @JvmStatic + fun getWeekdaysInMonth(startOfMonth: Timestamp): Array { + val month = startOfMonth.toCalendar()[Calendar.MONTH] + 1 + val year = startOfMonth.toCalendar()[Calendar.YEAR] + val weekday = startOfMonth.weekday + val monthLength = YearMonth.of(year, month).lengthOfMonth() + + val freq = Array(7) { 0 } + for (day in weekday until weekday + monthLength) { + freq[day % 7] += 1 + } + + return freq + } + @JvmStatic fun getToday(): Timestamp = Timestamp(getStartOfToday()) diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt index 75280ab9b..22b6e0057 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/DateUtilsTest.kt @@ -118,6 +118,31 @@ class DateUtilsTest : BaseUnitTest() { assertThat(arrayOf("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"), equalTo(longWeekdayNames)) } + @Test + fun getWeekdaysInMonth() { + val february = GregorianCalendar(2018, Calendar.FEBRUARY, 1) + val leapFebruary = GregorianCalendar(2020, Calendar.FEBRUARY, 1) + val month = GregorianCalendar(2020, Calendar.APRIL, 1) + val longMonth = GregorianCalendar(2020, Calendar.AUGUST, 1) + + assertThat( + arrayOf(4, 4, 4, 4, 4, 4, 4), + equalTo(DateUtils.getWeekdaysInMonth(Timestamp(february))) + ) + assertThat( + arrayOf(5, 4, 4, 4, 4, 4, 4), + equalTo(DateUtils.getWeekdaysInMonth(Timestamp(leapFebruary))) + ) + assertThat( + arrayOf(4, 4, 4, 4, 5, 5, 4), + equalTo(DateUtils.getWeekdaysInMonth(Timestamp(month))) + ) + assertThat( + arrayOf(5, 5, 5, 4, 4, 4, 4), + equalTo(DateUtils.getWeekdaysInMonth(Timestamp(longMonth))) + ) + } + @Test fun testGetToday() { setFixedLocalTime(FIXED_LOCAL_TIME)