diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/ScoreChartTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/ScoreChartTest.java index 2bccb6a00..d68ddb526 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/ScoreChartTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/ScoreChartTest.java @@ -19,20 +19,16 @@ package org.isoron.uhabits.activities.common.views; +import androidx.test.ext.junit.runners.*; import androidx.test.filters.*; -import androidx.test.runner.*; - -import androidx.test.ext.junit.runners.AndroidJUnit4; import org.isoron.uhabits.*; +import org.isoron.uhabits.activities.habits.show.views.*; import org.isoron.uhabits.core.models.*; -import org.isoron.uhabits.core.utils.*; import org.isoron.uhabits.utils.*; import org.junit.*; import org.junit.runner.*; -import java.util.*; - @RunWith(AndroidJUnit4.class) @MediumTest public class ScoreChartTest extends BaseViewTest @@ -43,6 +39,8 @@ public class ScoreChartTest extends BaseViewTest private ScoreChart view; + private ScoreCardPresenter presenter; + @Override @Before public void setUp() @@ -51,15 +49,13 @@ public class ScoreChartTest extends BaseViewTest fixtures.purgeHabits(habitList); habit = fixtures.createLongHabit(); - - Timestamp today = DateUtils.getTodayWithOffset(); - List known = habit.getComputedEntries().getKnown(); - Timestamp oldest = known.get(known.size() - 1).getTimestamp(); + presenter = new ScoreCardPresenter(habit, prefs.getFirstWeekday()); + ScoreCardViewModel model = presenter.present(0); view = new ScoreChart(targetContext); - view.setScores(habit.getScores().getByInterval(oldest, today)); - view.setColor(PaletteUtilsKt.toThemedAndroidColor(habit.getColor(), targetContext)); - view.setBucketSize(7); + view.setScores(model.getScores()); + view.setColor(PaletteUtilsKt.toFixedAndroidColor(model.getColor())); + view.setBucketSize(model.getBucketSize()); measureView(view, dpToPixels(300), dpToPixels(200)); } @@ -88,8 +84,9 @@ public class ScoreChartTest extends BaseViewTest @Test public void testRender_withMonthlyBucket() throws Throwable { - view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SUNDAY)); - view.setBucketSize(30); + ScoreCardViewModel model = presenter.present(2); + view.setScores(model.getScores()); + view.setBucketSize(model.getBucketSize()); view.invalidate(); assertRenders(view, BASE_PATH + "renderMonthly.png"); @@ -105,8 +102,9 @@ public class ScoreChartTest extends BaseViewTest @Test public void testRender_withYearlyBucket() throws Throwable { - view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.YEAR, Calendar.SUNDAY)); - view.setBucketSize(365); + ScoreCardViewModel model = presenter.present(4); + view.setScores(model.getScores()); + view.setBucketSize(model.getBucketSize()); view.invalidate(); assertRenders(view, BASE_PATH + "renderYearly.png"); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.kt index f22febedb..8687f9fbc 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.kt @@ -67,6 +67,7 @@ class ScoreCardPresenter( val BUCKET_SIZES = intArrayOf(1, 7, 31, 92, 365) fun getTruncateField(bucketSize: Int): DateUtils.TruncateField { when (bucketSize) { + 1 -> return DAY 7 -> return WEEK_NUMBER 31 -> return MONTH 92 -> return QUARTER @@ -78,12 +79,20 @@ class ScoreCardPresenter( fun present(spinnerPosition: Int): ScoreCardViewModel { val bucketSize = BUCKET_SIZES[spinnerPosition] - val scoreList = habit.scores val today = DateUtils.getTodayWithOffset() val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today - val scores = if (bucketSize == 1) scoreList.getByInterval(oldest, today) - else scoreList.groupBy(getTruncateField(bucketSize), firstWeekday) + val field = getTruncateField(bucketSize) + val scores = habit.scores.getByInterval(oldest, today).groupBy { + DateUtils.truncate(field, it.timestamp, firstWeekday) + }.map { (timestamp, scores) -> + Score(timestamp, scores.map { + it.value + }.average()) + }.sortedBy { + it.timestamp + }.reversed() + return ScoreCardViewModel( color = habit.color, scores = scores, @@ -91,5 +100,4 @@ class ScoreCardPresenter( spinnerPosition = spinnerPosition, ) } - } \ No newline at end of file diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/ScoreWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/ScoreWidget.kt index 0efc7c496..a3fae10f9 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/ScoreWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/ScoreWidget.kt @@ -38,23 +38,16 @@ class ScoreWidget( pendingIntentFactory.showHabit(habit) override fun refreshData(view: View) { - val size = ScoreCardPresenter.BUCKET_SIZES[prefs.scoreCardSpinnerPosition] - val today = DateUtils.getTodayWithOffset() - val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today - - val scores = when(size) { - 1 -> habit.scores.getByInterval(oldest, today) - else -> habit.scores.groupBy(ScoreCardPresenter.getTruncateField(size), prefs.firstWeekday) - } - + val presenter = ScoreCardPresenter(habit, prefs.firstWeekday) + val viewModel = presenter.present(prefs.scoreCardSpinnerPosition) val widgetView = view as GraphWidgetView widgetView.setBackgroundAlpha(preferedBackgroundAlpha) if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) (widgetView.dataView as ScoreChart).apply { setIsTransparencyEnabled(true) - setBucketSize(size) + setBucketSize(viewModel.bucketSize) setColor(habit.color.toThemedAndroidColor(context)) - setScores(scores) + setScores(viewModel.scores) } } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.kt index b30a9d233..81ea9674a 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.kt +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.kt @@ -77,7 +77,7 @@ data class Habit( isNumerical = isNumerical, ) - val to = DateUtils.getTodayWithOffset() + val to = DateUtils.getTodayWithOffset().plus(30) val entries = computedEntries.getKnown() var from = entries.lastOrNull()?.timestamp ?: to if (from.isNewerThan(to)) from = to diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java index 3d9148c8b..b2ecb84ca 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java @@ -21,8 +21,6 @@ package org.isoron.uhabits.core.models; import androidx.annotation.*; -import org.isoron.uhabits.core.utils.*; - import java.util.*; import static org.isoron.uhabits.core.models.Entry.*; @@ -68,22 +66,13 @@ public class ScoreList return result; } - public List groupBy(DateUtils.TruncateField field, int firstWeekday) - { - HashMap> groups = getGroupedValues(field, firstWeekday); - List scores = groupsToAvgScores(groups); - Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1)); - return scores; - } - public void recompute( Frequency frequency, boolean isNumerical, double targetValue, EntryList computedEntries, Timestamp from, - Timestamp to - ) + Timestamp to) { list.clear(); if (computedEntries.getKnown().isEmpty()) return; @@ -139,47 +128,4 @@ public class ScoreList list.put(timestamp, new Score(timestamp, previousValue)); } } - - - @NonNull - private HashMap> getGroupedValues(DateUtils.TruncateField field, - int firstWeekday) - { - HashMap> groups = new HashMap<>(); - - for (Score s : list.values()) - { - Timestamp groupTimestamp = new Timestamp( - DateUtils.truncate( - field, - s.getTimestamp().getUnixTime(), - firstWeekday)); - - if (!groups.containsKey(groupTimestamp)) - groups.put(groupTimestamp, new ArrayList<>()); - - groups.get(groupTimestamp).add(s.getValue()); - } - - return groups; - } - - @NonNull - private List groupsToAvgScores(HashMap> groups) - { - List scores = new LinkedList<>(); - - for (Timestamp timestamp : groups.keySet()) - { - double meanValue = 0.0; - ArrayList groupValues = groups.get(timestamp); - - for (Double v : groupValues) meanValue += v; - meanValue /= groupValues.size(); - - scores.add(new Score(timestamp, meanValue)); - } - - return scores; - } } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java index 7d4abb242..aa3e89a54 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java @@ -266,6 +266,13 @@ public abstract class DateUtils return Locale.getDefault(); } + public static Timestamp truncate(TruncateField field, + Timestamp timestamp, + int firstWeekday) + { + return new Timestamp(truncate(field, timestamp.getUnixTime(), firstWeekday)); + } + public static Long truncate(TruncateField field, long timestamp, int firstWeekday) @@ -275,6 +282,9 @@ public abstract class DateUtils switch (field) { + case DAY: + return cal.getTimeInMillis(); + case MONTH: cal.set(DAY_OF_MONTH, 1); return cal.getTimeInMillis(); @@ -318,6 +328,6 @@ public abstract class DateUtils public enum TruncateField { - MONTH, WEEK_NUMBER, YEAR, QUARTER + DAY, MONTH, WEEK_NUMBER, YEAR, QUARTER } } diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java index 3ded24c90..7031f574b 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java @@ -222,19 +222,6 @@ public class ScoreListTest extends BaseUnitTest assertThat(habit.getScores().get(today).getValue(), greaterThan(0.99)); } - @Test - public void test_groupBy() - { - Habit habit = fixtures.createLongHabit(); - List list = - habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SATURDAY); - - assertThat(list.size(), equalTo(5)); - assertThat(list.get(0).getValue(), closeTo(0.644120, E)); - assertThat(list.get(1).getValue(), closeTo(0.713651, E)); - assertThat(list.get(2).getValue(), closeTo(0.571922, E)); - } - @Test public void test_recompute() {