From 51b95178973805549fb756892d22817c647a7dd8 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Wed, 30 Dec 2020 17:28:10 -0600 Subject: [PATCH 1/4] EntryList: remove getValues --- .../common/views/HistoryChartTest.java | 10 +++++- .../habits/list/views/HabitCardViewTest.kt | 18 ++++++++-- .../common/dialogs/HistoryEditorDialog.java | 8 ++++- .../habits/show/views/HistoryCard.kt | 21 ++++++++---- .../isoron/uhabits/widgets/HistoryWidget.kt | 16 ++++++--- .../uhabits/core/io/HabitsCSVExporter.kt | 5 +-- .../isoron/uhabits/core/models/EntryList.kt | 33 ------------------- .../habits/list/HabitCardListCache.java | 10 ++++-- .../core/ui/widgets/WidgetBehavior.java | 4 +-- .../uhabits/core/models/EntryListTest.kt | 21 ------------ .../habits/list/HabitCardListCacheTest.java | 7 +++- 11 files changed, 74 insertions(+), 79 deletions(-) diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java index 57835707f..ac16fa3a9 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java @@ -22,6 +22,7 @@ package org.isoron.uhabits.activities.common.views; import androidx.test.ext.junit.runners.*; import androidx.test.filters.*; +import org.apache.commons.lang3.*; import org.isoron.uhabits.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.ui.callbacks.*; @@ -56,9 +57,16 @@ public class HistoryChartTest extends BaseViewTest habit = fixtures.createLongHabit(); today = new Timestamp(DateUtils.getStartOfToday()); + Integer[] entries = habit + .getComputedEntries() + .getByInterval(today.minus(300), today) + .stream() + .map(Entry::getValue) + .toArray(Integer[]::new); + chart = new HistoryChart(targetContext); chart.setSkipEnabled(true); - chart.setEntries(habit.getComputedEntries().getAllValues()); + chart.setEntries(ArrayUtils.toPrimitive(entries)); chart.setColor(PaletteUtilsKt.toFixedAndroidColor(habit.getColor())); measureView(chart, dpToPixels(400), dpToPixels(200)); diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewTest.kt index 33586f756..bdd0658ec 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewTest.kt @@ -25,6 +25,7 @@ import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.R import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.utils.DateUtils import org.junit.Test import org.junit.runner.RunWith @@ -37,6 +38,7 @@ class HabitCardViewTest : BaseViewTest() { private lateinit var view: HabitCardView private lateinit var habit1: Habit private lateinit var habit2: Habit + private lateinit var today: Timestamp override fun setUp() { super.setUp() @@ -44,11 +46,16 @@ class HabitCardViewTest : BaseViewTest() { habit1 = fixtures.createLongHabit() habit2 = fixtures.createLongNumericalHabit() - val today = DateUtils.getTodayWithOffset() + today = DateUtils.getTodayWithOffset() + + val entries = habit1 + .computedEntries + .getByInterval(today.minus(300), today) + .map { it.value }.toIntArray() view = component.getHabitCardViewFactory().create().apply { habit = habit1 - values = habit1.computedEntries.getAllValues() + values = entries score = habit1.scores.get(today).value isSelected = false buttonCount = 5 @@ -73,9 +80,14 @@ class HabitCardViewTest : BaseViewTest() { @Test fun testRender_numerical() { + val entries = habit2 + .computedEntries + .getByInterval(today.minus(300), today) + .map { it.value }.toIntArray() + view.apply { habit = habit2 - values = habit2.computedEntries.getAllValues() + values = entries } assertRenders(view, "$PATH/render_numerical.png") } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java index deca1611a..72fad14b1 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java @@ -31,6 +31,7 @@ import android.util.*; import org.isoron.uhabits.*; import org.isoron.uhabits.activities.common.views.*; +import org.isoron.uhabits.activities.habits.show.views.*; import org.isoron.uhabits.core.commands.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.preferences.*; @@ -178,7 +179,12 @@ public class HistoryEditorDialog extends AppCompatDialogFragment @Override public void doInBackground() { - checkmarks = habit.getComputedEntries().getAllValues(); + HistoryCardViewModel model = new HistoryCardPresenter( + habit, + prefs.getFirstWeekday(), + prefs.isSkipEnabled() + ).present(); + checkmarks = model.getEntries(); } @Override diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.kt index bcea34644..2627d9eda 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.kt @@ -24,6 +24,7 @@ import android.view.LayoutInflater import android.widget.LinearLayout import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.utils.DateUtils import org.isoron.uhabits.databinding.ShowHabitHistoryBinding import org.isoron.uhabits.utils.toThemedAndroidColor @@ -63,11 +64,17 @@ class HistoryCardPresenter( val firstWeekday: Int, val isSkipEnabled: Boolean, ) { - fun present() = HistoryCardViewModel( - entries = habit.computedEntries.getAllValues(), - color = habit.color, - firstWeekday = firstWeekday, - isNumerical = habit.isNumerical, - isSkipEnabled = isSkipEnabled, - ) + fun present(): HistoryCardViewModel { + val today = DateUtils.getTodayWithOffset() + val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today + val entries = habit.computedEntries.getByInterval(oldest, today).map { it.value }.toIntArray() + + return HistoryCardViewModel( + entries = entries, + color = habit.color, + firstWeekday = firstWeekday, + isNumerical = habit.isNumerical, + isSkipEnabled = isSkipEnabled, + ) + } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt index 52a932339..eeace136e 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt @@ -23,6 +23,7 @@ import android.app.PendingIntent import android.content.Context import android.view.View import org.isoron.uhabits.activities.common.views.HistoryChart +import org.isoron.uhabits.activities.habits.show.views.HistoryCardPresenter import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.utils.toThemedAndroidColor import org.isoron.uhabits.widgets.views.GraphWidgetView @@ -42,12 +43,17 @@ class HistoryWidget( val widgetView = view as GraphWidgetView widgetView.setBackgroundAlpha(preferedBackgroundAlpha) if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) + val model = HistoryCardPresenter( + habit = habit, + isSkipEnabled = prefs.isSkipEnabled, + firstWeekday = prefs.firstWeekday, + ).present() (widgetView.dataView as HistoryChart).apply { - setFirstWeekday(firstWeekday) - setSkipEnabled(prefs.isSkipEnabled) - setColor(habit.color.toThemedAndroidColor(context)) - setEntries(habit.computedEntries.getAllValues()) - setNumerical(habit.isNumerical) + setFirstWeekday(model.firstWeekday) + setSkipEnabled(model.isSkipEnabled) + setColor(model.color.toThemedAndroidColor(context)) + setEntries(model.entries) + setNumerical(model.isNumerical) } } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/io/HabitsCSVExporter.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/io/HabitsCSVExporter.kt index 2cd7dbcd4..2b73b25e6 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/io/HabitsCSVExporter.kt +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/io/HabitsCSVExporter.kt @@ -18,6 +18,7 @@ */ package org.isoron.uhabits.core.io +import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.models.EntryList import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.HabitList @@ -148,10 +149,10 @@ class HabitsCSVExporter( val timeframe = getTimeframe() val oldest = timeframe[0] val newest = DateUtils.getToday() - val checkmarks: MutableList = ArrayList() + val checkmarks: MutableList> = ArrayList() val scores: MutableList> = ArrayList() for (habit in selectedHabits) { - checkmarks.add(habit.computedEntries.getValues(oldest, newest)) + checkmarks.add(ArrayList(habit.computedEntries.getByInterval(oldest, newest))) scores.add(ArrayList(habit.scores.getByInterval(oldest, newest))) } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/EntryList.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/EntryList.kt index 2b1999259..b9a9dd98f 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/EntryList.kt +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/EntryList.kt @@ -189,39 +189,6 @@ open class EntryList { return map } - /** - * Returns the values of the entries that fall inside a certain interval of time. The values - * are returned in an array containing one integer value for each day of the interval. The - * first entry corresponds to the most recent day in the interval. Each subsequent entry - * corresponds to one day older than the previous entry. The boundaries of the time interval - * are included. - */ - @Deprecated("") - @Synchronized - fun getValues(from: Timestamp, to: Timestamp): IntArray { - if (from.isNewerThan(to)) throw IllegalArgumentException() - val nDays = from.daysUntil(to) + 1 - val result = IntArray(nDays) { UNKNOWN } - getKnown().filter { entry -> - !entry.timestamp.isNewerThan(to) && !entry.timestamp.isOlderThan(from) - }.forEach { entry -> - val offset = entry.timestamp.daysUntil(to) - result[offset] = entry.value - } - return result - } - - @Deprecated("") - @Synchronized - fun getAllValues(): IntArray { - val entries = getKnown() - if (entries.isEmpty()) return IntArray(0) - var (fromTimestamp, _) = entries.last() - val toTimestamp = DateUtils.getTodayWithOffset() - if (fromTimestamp.isNewerThan(toTimestamp)) fromTimestamp = toTimestamp - return getValues(fromTimestamp, toTimestamp) - } - @Deprecated("") @Synchronized open fun getThisWeekValue(firstWeekday: Int, isNumerical: Boolean): Int { diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.java index c9de67bf1..336ec3160 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.java @@ -21,6 +21,7 @@ package org.isoron.uhabits.core.ui.screens.habits.list; import androidx.annotation.*; +import org.apache.commons.lang3.*; import org.isoron.uhabits.core.*; import org.isoron.uhabits.core.commands.*; import org.isoron.uhabits.core.models.*; @@ -363,9 +364,12 @@ public class HabitCardListCache implements CommandRunner.Listener if (targetId != null && !targetId.equals(id)) continue; newData.scores.put(id, habit.getScores().get(today).getValue()); - newData.checkmarks.put( - id, - habit.getComputedEntries().getValues(dateFrom, today)); + Integer[] entries = habit.getComputedEntries() + .getByInterval(dateFrom, today) + .stream() + .map(Entry::getValue) + .toArray(Integer[]::new); + newData.checkmarks.put(id, ArrayUtils.toPrimitive(entries)); runner.publishProgress(this, position); } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java index 99fb7aced..546928029 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/widgets/WidgetBehavior.java @@ -81,13 +81,13 @@ public class WidgetBehavior } public void onIncrement(@NotNull Habit habit, @NotNull Timestamp timestamp, int amount) { - int currentValue = habit.getComputedEntries().getValues(timestamp, timestamp)[0]; + int currentValue = habit.getComputedEntries().get(timestamp).getValue(); setValue(habit, timestamp, currentValue + amount); notificationTray.cancel(habit); } public void onDecrement(@NotNull Habit habit, @NotNull Timestamp timestamp, int amount) { - int currentValue = habit.getComputedEntries().getValues(timestamp, timestamp)[0]; + int currentValue = habit.getComputedEntries().get(timestamp).getValue(); setValue(habit, timestamp, currentValue - amount); notificationTray.cancel(habit); } diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/EntryListTest.kt b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/EntryListTest.kt index aa27c7996..f350b2ea6 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/EntryListTest.kt +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/EntryListTest.kt @@ -65,27 +65,6 @@ class EntryListTest { assertEquals(Entry(today.minus(5), 20), actual[5]) } - @Test - fun testGetValues() { - val entries = EntryList() - val today = DateUtils.getToday() - - entries.add(Entry(today.minus(3), YES_MANUAL)) - entries.add(Entry(today.minus(5), YES_MANUAL)) - entries.add(Entry(today.minus(6), YES_MANUAL)) - - val expected = intArrayOf( - UNKNOWN, // 1 - UNKNOWN, // 2 - YES_MANUAL, // 3 - UNKNOWN, // 4 - YES_MANUAL, // 5 - YES_MANUAL, // 6 - UNKNOWN, // 7 - ) - assertThat(entries.getValues(today.minus(7), today.minus(1)), equalTo(expected)) - } - @Test fun testComputeBoolean() { val today = DateUtils.getToday() diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCacheTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCacheTest.java index 2cf3e979f..3ecce0623 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCacheTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCacheTest.java @@ -19,6 +19,7 @@ package org.isoron.uhabits.core.ui.screens.habits.list; +import org.apache.commons.lang3.*; import org.isoron.uhabits.core.*; import org.isoron.uhabits.core.commands.*; import org.isoron.uhabits.core.models.*; @@ -104,7 +105,11 @@ public class HabitCardListCacheTest extends BaseUnitTest assertThat(cache.getScore(h.getId()), equalTo(score)); int[] actualCheckmarks = cache.getCheckmarks(h.getId()); - int[] expectedCheckmarks = h.getComputedEntries().getValues(today.minus(9), today); + int[] expectedCheckmarks = ArrayUtils.toPrimitive(h.getComputedEntries() + .getByInterval(today.minus(9), today) + .stream() + .map(Entry::getValue) + .toArray(Integer[]::new)); assertThat(actualCheckmarks, equalTo(expectedCheckmarks)); } From 1cdbc53dc5d4284350a3466b5f35cb37f6ef6395 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Wed, 30 Dec 2020 22:45:33 -0600 Subject: [PATCH 2/4] EntryList: simplify groupBy --- .../activities/habits/show/views/BarCard.kt | 45 ++++--- .../habits/show/views/TargetCard.kt | 50 +++++-- .../isoron/uhabits/core/models/EntryList.kt | 127 ++++++------------ .../isoron/uhabits/core/models/Timestamp.java | 3 + .../core/models/sqlite/SQLiteEntryList.kt | 11 -- .../uhabits/core/models/EntryListTest.kt | 36 ++--- 6 files changed, 119 insertions(+), 153 deletions(-) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.kt index 56d6df3c2..813244a60 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.kt @@ -28,6 +28,7 @@ import org.isoron.uhabits.activities.habits.show.views.ScoreCardPresenter.Compan import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.groupedSum import org.isoron.uhabits.core.utils.DateUtils import org.isoron.uhabits.databinding.ShowHabitBarBinding import org.isoron.uhabits.utils.toThemedAndroidColor @@ -61,20 +62,33 @@ class BarCard(context: Context, attrs: AttributeSet) : LinearLayout(context, att binding.numericalSpinner.onItemSelectedListener = null binding.numericalSpinner.setSelection(data.numericalSpinnerPosition) - binding.numericalSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - onNumericalSpinnerPosition(position) - } - override fun onNothingSelected(parent: AdapterView<*>?) { + binding.numericalSpinner.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + onNumericalSpinnerPosition(position) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + } } - } binding.boolSpinner.onItemSelectedListener = null binding.boolSpinner.setSelection(data.boolSpinnerPosition) binding.boolSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { onBoolSpinnerPosition(position) } + override fun onNothingSelected(parent: AdapterView<*>?) { } } @@ -99,18 +113,11 @@ class BarCardPresenter( } val today = DateUtils.getToday() val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today - val entries = if (bucketSize == 1) { - habit.computedEntries.getByInterval(oldest, today).map { - if (it.value < 0) Entry(it.timestamp, 0) else it - } - } else { - habit.computedEntries.groupBy( - original = habit.computedEntries.getByInterval(oldest, today), - field = getTruncateField(bucketSize), - firstWeekday = firstWeekday, - isNumerical = habit.isNumerical, - ) - } + val entries = habit.computedEntries.getByInterval(oldest, today).groupedSum( + truncateField = getTruncateField(bucketSize), + firstWeekday = firstWeekday, + isNumerical = habit.isNumerical, + ) return BarCardViewModel( entries = entries, bucketSize = bucketSize, diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.kt index ff44ac4cd..4add0f185 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.kt @@ -28,7 +28,13 @@ import kotlinx.coroutines.invoke import org.isoron.uhabits.R import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.groupedSum import org.isoron.uhabits.core.utils.DateUtils +import org.isoron.uhabits.core.utils.DateUtils.TruncateField.DAY +import org.isoron.uhabits.core.utils.DateUtils.TruncateField.MONTH +import org.isoron.uhabits.core.utils.DateUtils.TruncateField.QUARTER +import org.isoron.uhabits.core.utils.DateUtils.TruncateField.WEEK_NUMBER +import org.isoron.uhabits.core.utils.DateUtils.TruncateField.YEAR import org.isoron.uhabits.databinding.ShowHabitTargetBinding import org.isoron.uhabits.utils.toThemedAndroidColor import java.util.ArrayList @@ -61,12 +67,34 @@ class TargetCardPresenter( ) { suspend fun present(): TargetCardViewModel = Dispatchers.IO { val today = DateUtils.getTodayWithOffset() - val entries = habit.computedEntries - val valueToday = entries.get(today).value / 1e3 - val valueThisWeek = entries.getThisWeekValue(firstWeekday, habit.isNumerical) / 1e3 - val valueThisMonth = entries.getThisMonthValue(habit.isNumerical) / 1e3 - val valueThisQuarter = entries.getThisQuarterValue(habit.isNumerical) / 1e3 - val valueThisYear = entries.getThisYearValue(habit.isNumerical) / 1e3 + val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today + val entries = habit.computedEntries.getByInterval(oldest, today) + + val valueToday = entries.groupedSum( + truncateField = DAY, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + + val valueThisWeek = entries.groupedSum( + truncateField = WEEK_NUMBER, + firstWeekday = firstWeekday, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + + val valueThisMonth = entries.groupedSum( + truncateField = MONTH, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + + val valueThisQuarter = entries.groupedSum( + truncateField = QUARTER, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + + val valueThisYear = entries.groupedSum( + truncateField = YEAR, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 val cal = DateUtils.getStartOfTodayCalendarWithOffset() val daysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH) @@ -80,11 +108,11 @@ class TargetCardPresenter( val targetThisYear = targetToday * daysInYear val values = ArrayList() - if (habit.frequency.denominator <= 1) values.add(valueToday) - if (habit.frequency.denominator <= 7) values.add(valueThisWeek) - values.add(valueThisMonth) - values.add(valueThisQuarter) - values.add(valueThisYear) + if (habit.frequency.denominator <= 1) values.add(valueToday / 1e3) + if (habit.frequency.denominator <= 7) values.add(valueThisWeek / 1e3) + values.add(valueThisMonth / 1e3) + values.add(valueThisQuarter / 1e3) + values.add(valueThisYear / 1e3) val targets = ArrayList() if (habit.frequency.denominator <= 1) targets.add(targetToday) diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/EntryList.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/EntryList.kt index b9a9dd98f..57d96deac 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/EntryList.kt +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/EntryList.kt @@ -28,6 +28,7 @@ import java.util.ArrayList import java.util.Calendar import javax.annotation.concurrent.ThreadSafe import kotlin.collections.set +import kotlin.math.max import kotlin.math.min @ThreadSafe @@ -79,45 +80,6 @@ open class EntryList { return entriesByTimestamp.values.sortedBy { it.timestamp }.reversed() } - /** - * Truncates the timestamps of all known entries, then aggregates their values. This function - * is used to generate bar plots where each bar shows the number of repetitions in a given week, - * month or year. - * - * For boolean habits, the value of the aggregated entry equals to the number of YES_MANUAL - * entries. For numerical habits, the value is the total sum. The field [firstWeekday] is only - * relevant when grouping by week. - */ - @Synchronized - open fun groupBy( - original: List, - field: DateUtils.TruncateField, - firstWeekday: Int, - isNumerical: Boolean, - ): List { - val truncated = original.map { - Entry(it.timestamp.truncate(field, firstWeekday), it.value) - } - val timestamps = mutableListOf() - val values = mutableListOf() - for (i in truncated.indices) { - if (i == 0 || timestamps.last() != truncated[i].timestamp) { - timestamps.add(truncated[i].timestamp) - values.add(0) - } - if (isNumerical) { - if (truncated[i].value > 0) { - values[values.lastIndex] += truncated[i].value - } - } else { - if (truncated[i].value == YES_MANUAL) { - values[values.lastIndex] += 1000 - } - } - } - return timestamps.indices.map { Entry(timestamps[it], values[it]) } - } - /** * Replaces all entries in this list by entries computed automatically from another list. * @@ -189,55 +151,6 @@ open class EntryList { return map } - @Deprecated("") - @Synchronized - open fun getThisWeekValue(firstWeekday: Int, isNumerical: Boolean): Int { - return getThisIntervalValue( - truncateField = DateUtils.TruncateField.WEEK_NUMBER, - firstWeekday = firstWeekday, - isNumerical = isNumerical - ) - } - - @Deprecated("") - @Synchronized - open fun getThisMonthValue(isNumerical: Boolean): Int { - return getThisIntervalValue( - truncateField = DateUtils.TruncateField.MONTH, - firstWeekday = Calendar.SATURDAY, - isNumerical = isNumerical - ) - } - - @Deprecated("") - @Synchronized - open fun getThisQuarterValue(isNumerical: Boolean): Int { - return getThisIntervalValue( - truncateField = DateUtils.TruncateField.QUARTER, - firstWeekday = Calendar.SATURDAY, - isNumerical = isNumerical - ) - } - - @Deprecated("") - @Synchronized - open fun getThisYearValue(isNumerical: Boolean): Int { - return getThisIntervalValue( - truncateField = DateUtils.TruncateField.YEAR, - firstWeekday = Calendar.SATURDAY, - isNumerical = isNumerical - ) - } - - private fun getThisIntervalValue( - truncateField: DateUtils.TruncateField, - firstWeekday: Int, - isNumerical: Boolean, - ): Int { - val groups: List = groupBy(getKnown(), truncateField, firstWeekday, isNumerical) - return if (groups.isEmpty()) 0 else groups[0].value - } - data class Interval(val begin: Timestamp, val center: Timestamp, val end: Timestamp) { val length: Int get() = begin.daysUntil(end) + 1 @@ -344,3 +257,41 @@ open class EntryList { } } } + +/** + * Given a list of entries, truncates the timestamp of each entry (according to the field given), + * groups the entries according to this truncated timestamp, then creates a new entry (t,v) for + * each group, where t is the truncated timestamp and v is the sum of the values of all entries in + * the group. + * + * For numerical habits, non-positive entry values are converted to zero. For boolean habits, each + * YES_MANUAL value is converted to 1000 and all other values are converted to zero. + * + * The returned list is sorted by timestamp, with the newest entry coming first and the oldest entry + * coming last. If the original list has gaps in it (for example, weeks or months without any + * entries), then the list produced by this method will also have gaps. + * + * The argument [firstWeekday] is only relevant when truncating by week. + */ +fun List.groupedSum( + truncateField: DateUtils.TruncateField, + firstWeekday: Int = Calendar.SATURDAY, + isNumerical: Boolean, +): List { + return this.map { (timestamp, value) -> + if (isNumerical) { + Entry(timestamp, max(0, value)) + } else { + Entry(timestamp, if (value == YES_MANUAL) 1000 else 0) + } + }.groupBy { entry -> + entry.timestamp.truncate( + truncateField, + firstWeekday, + ) + }.entries.map { (timestamp, entries) -> + Entry(timestamp, entries.sumOf { it.value }) + }.sortedBy { (timestamp, _) -> + - timestamp.unixTime + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Timestamp.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Timestamp.java index 992894b8b..45a3b387b 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Timestamp.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Timestamp.java @@ -21,9 +21,12 @@ package org.isoron.uhabits.core.models; import org.apache.commons.lang3.builder.*; import org.isoron.uhabits.core.utils.*; +import org.jetbrains.annotations.*; import java.util.*; +import kotlin.*; + import static java.util.Calendar.*; public final class Timestamp implements Comparable diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/sqlite/SQLiteEntryList.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/sqlite/SQLiteEntryList.kt index dbea262a5..f2d9292d7 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/sqlite/SQLiteEntryList.kt +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/sqlite/SQLiteEntryList.kt @@ -26,7 +26,6 @@ import org.isoron.uhabits.core.models.EntryList import org.isoron.uhabits.core.models.Frequency import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.models.sqlite.records.EntryRecord -import org.isoron.uhabits.core.utils.DateUtils class SQLiteEntryList(database: Database) : EntryList() { val repository = Repository(EntryRecord::class.java, database) @@ -79,16 +78,6 @@ class SQLiteEntryList(database: Database) : EntryList() { return super.getKnown() } - override fun groupBy( - original: List, - field: DateUtils.TruncateField, - firstWeekday: Int, - isNumerical: Boolean - ): List { - loadRecords() - return super.groupBy(original, field, firstWeekday, isNumerical) - } - override fun recomputeFrom(originalEntries: EntryList, frequency: Frequency, isNumerical: Boolean) { throw UnsupportedOperationException() } diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/EntryListTest.kt b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/EntryListTest.kt index f350b2ea6..7882a9d48 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/EntryListTest.kt +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/EntryListTest.kt @@ -142,10 +142,8 @@ class EntryListTest { entries.add(Entry(reference.minus(offsets[it]), values[it])) } - val byMonth = entries.groupBy( - original = entries.getKnown(), - field = DateUtils.TruncateField.MONTH, - firstWeekday = Calendar.SATURDAY, + val byMonth = entries.getKnown().groupedSum( + truncateField = DateUtils.TruncateField.MONTH, isNumerical = true, ) assertThat(byMonth.size, equalTo(17)) @@ -153,10 +151,8 @@ class EntryListTest { assertThat(byMonth[6], equalTo(Entry(Timestamp.from(2013, Calendar.DECEMBER, 1), 1988))) assertThat(byMonth[12], equalTo(Entry(Timestamp.from(2013, Calendar.MAY, 1), 1271))) - val byQuarter = entries.groupBy( - original = entries.getKnown(), - field = DateUtils.TruncateField.QUARTER, - firstWeekday = Calendar.SATURDAY, + val byQuarter = entries.getKnown().groupedSum( + truncateField = DateUtils.TruncateField.QUARTER, isNumerical = true, ) assertThat(byQuarter.size, equalTo(6)) @@ -164,10 +160,8 @@ class EntryListTest { assertThat(byQuarter[3], equalTo(Entry(Timestamp.from(2013, Calendar.JULY, 1), 3838))) assertThat(byQuarter[5], equalTo(Entry(Timestamp.from(2013, Calendar.JANUARY, 1), 4975))) - val byYear = entries.groupBy( - original = entries.getKnown(), - field = DateUtils.TruncateField.YEAR, - firstWeekday = Calendar.SATURDAY, + val byYear = entries.getKnown().groupedSum( + truncateField = DateUtils.TruncateField.YEAR, isNumerical = true, ) assertThat(byYear.size, equalTo(2)) @@ -192,10 +186,8 @@ class EntryListTest { entries.add(Entry(reference.minus(offsets[it]), YES_MANUAL)) } - val byMonth = entries.groupBy( - original = entries.getKnown(), - field = DateUtils.TruncateField.MONTH, - firstWeekday = Calendar.SATURDAY, + val byMonth = entries.getKnown().groupedSum( + truncateField = DateUtils.TruncateField.MONTH, isNumerical = false, ) assertThat(byMonth.size, equalTo(17)) @@ -203,10 +195,8 @@ class EntryListTest { assertThat(byMonth[6], equalTo(Entry(Timestamp.from(2013, Calendar.DECEMBER, 1), 7_000))) assertThat(byMonth[12], equalTo(Entry(Timestamp.from(2013, Calendar.MAY, 1), 6_000))) - val byQuarter = entries.groupBy( - original = entries.getKnown(), - field = DateUtils.TruncateField.QUARTER, - firstWeekday = Calendar.SATURDAY, + val byQuarter = entries.getKnown().groupedSum( + truncateField = DateUtils.TruncateField.QUARTER, isNumerical = false, ) assertThat(byQuarter.size, equalTo(6)) @@ -214,10 +204,8 @@ class EntryListTest { assertThat(byQuarter[3], equalTo(Entry(Timestamp.from(2013, Calendar.JULY, 1), 17_000))) assertThat(byQuarter[5], equalTo(Entry(Timestamp.from(2013, Calendar.JANUARY, 1), 20_000))) - val byYear = entries.groupBy( - original = entries.getKnown(), - field = DateUtils.TruncateField.YEAR, - firstWeekday = Calendar.SATURDAY, + val byYear = entries.getKnown().groupedSum( + truncateField = DateUtils.TruncateField.YEAR, isNumerical = false, ) assertThat(byYear.size, equalTo(2)) From 13826f4934e24bfd1a04ae79ddfcdf420cb5dc70 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Thu, 31 Dec 2020 00:10:29 -0600 Subject: [PATCH 3/4] ShowHabitActivity: Move presenters and view models to uhabits-core --- .../common/views/ScoreChartTest.java | 10 +- ...cyCardTest.kt => FrequencyCardViewTest.kt} | 9 +- ...toryCardTest.kt => HistoryCardViewTest.kt} | 11 +- .../habits/show/views/NotesCardViewTest.kt | 1 + .../habits/show/views/OverviewCardViewTest.kt | 1 + ...{ScoreCardTest.kt => ScoreCardViewTest.kt} | 15 ++- .../habits/show/views/StreakCardViewTest.kt | 1 + .../habits/show/views/SubtitleCardViewTest.kt | 11 +- .../common/dialogs/HistoryEditorDialog.java | 6 +- .../habits/show/ShowHabitActivity.kt | 24 ++-- .../activities/habits/show/ShowHabitView.kt | 92 +------------- .../show/views/{BarCard.kt => BarCardView.kt} | 52 +------- ...{FrequencyCard.kt => FrequencyCardView.kt} | 26 +--- .../{HistoryCard.kt => HistoryCardView.kt} | 34 +----- .../views/{NotesCard.kt => NotesCardView.kt} | 10 +- .../{OverviewCard.kt => OverviewCardView.kt} | 38 +----- .../activities/habits/show/views/ScoreCard.kt | 114 ------------------ .../habits/show/views/ScoreCardView.kt | 59 +++++++++ .../{StreakCard.kt => StreakCardView.kt} | 20 +-- .../{SubtitleCard.kt => SubtitleCardView.kt} | 49 ++------ .../habits/show/views/TargetCardView.kt | 52 ++++++++ .../isoron/uhabits/widgets/HistoryWidget.kt | 6 +- .../org/isoron/uhabits/widgets/ScoreWidget.kt | 10 +- .../isoron/uhabits/widgets/TargetWidget.kt | 9 +- .../src/main/res/layout/show_habit.xml | 8 +- .../core/ui/screens/habits/show/ShowHabit.kt | 106 ++++++++++++++++ .../ui/screens/habits/show/views/BarCard.kt | 68 +++++++++++ .../habits/show/views/FrequencyCard.kt | 44 +++++++ .../screens/habits/show/views/HistoryCard.kt | 53 ++++++++ .../ui/screens/habits/show/views/NotesCard.kt | 32 +++++ .../screens/habits/show/views/OverviewCard.kt | 56 +++++++++ .../ui/screens/habits/show/views/ScoreCard.kt | 79 ++++++++++++ .../screens/habits/show/views/StreakCart.kt | 38 ++++++ .../screens/habits/show/views/SubtitleCard.kt | 49 ++++++++ .../screens}/habits/show/views/TargetCard.kt | 72 ++++------- 35 files changed, 754 insertions(+), 511 deletions(-) rename android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/{FrequencyCardTest.kt => FrequencyCardViewTest.kt} (85%) rename android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/{HistoryCardTest.kt => HistoryCardViewTest.kt} (88%) rename android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/{ScoreCardTest.kt => ScoreCardViewTest.kt} (78%) rename android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/{BarCard.kt => BarCardView.kt} (62%) rename android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/{FrequencyCard.kt => FrequencyCardView.kt} (67%) rename android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/{HistoryCard.kt => HistoryCardView.kt} (63%) rename android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/{NotesCard.kt => NotesCardView.kt} (86%) rename android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/{OverviewCard.kt => OverviewCardView.kt} (65%) delete mode 100644 android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.kt create mode 100644 android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardView.kt rename android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/{StreakCard.kt => StreakCardView.kt} (73%) rename android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/{SubtitleCard.kt => SubtitleCardView.kt} (76%) create mode 100644 android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCardView.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabit.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/BarCard.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/FrequencyCard.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/NotesCard.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/OverviewCard.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/ScoreCard.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/StreakCart.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt rename android/{uhabits-android/src/main/java/org/isoron/uhabits/activities => uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens}/habits/show/views/TargetCard.kt (58%) 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 d68ddb526..6b45576b9 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 @@ -23,8 +23,8 @@ import androidx.test.ext.junit.runners.*; import androidx.test.filters.*; import org.isoron.uhabits.*; -import org.isoron.uhabits.activities.habits.show.views.*; import org.isoron.uhabits.core.models.*; +import org.isoron.uhabits.core.ui.screens.habits.show.views.*; import org.isoron.uhabits.utils.*; import org.junit.*; import org.junit.runner.*; @@ -49,8 +49,8 @@ public class ScoreChartTest extends BaseViewTest fixtures.purgeHabits(habitList); habit = fixtures.createLongHabit(); - presenter = new ScoreCardPresenter(habit, prefs.getFirstWeekday()); - ScoreCardViewModel model = presenter.present(0); + presenter = new ScoreCardPresenter(); + ScoreCardViewModel model = presenter.present(habit, prefs.getFirstWeekday(), 0); view = new ScoreChart(targetContext); view.setScores(model.getScores()); @@ -84,7 +84,7 @@ public class ScoreChartTest extends BaseViewTest @Test public void testRender_withMonthlyBucket() throws Throwable { - ScoreCardViewModel model = presenter.present(2); + ScoreCardViewModel model = presenter.present(habit, prefs.getFirstWeekday(), 2); view.setScores(model.getScores()); view.setBucketSize(model.getBucketSize()); view.invalidate(); @@ -102,7 +102,7 @@ public class ScoreChartTest extends BaseViewTest @Test public void testRender_withYearlyBucket() throws Throwable { - ScoreCardViewModel model = presenter.present(4); + ScoreCardViewModel model = presenter.present(habit, prefs.getFirstWeekday(), 4); view.setScores(model.getScores()); view.setBucketSize(model.getBucketSize()); view.invalidate(); diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardViewTest.kt similarity index 85% rename from android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardTest.kt rename to android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardViewTest.kt index ef40fb082..18ce75c1f 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardViewTest.kt @@ -24,15 +24,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.R +import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardPresenter import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @MediumTest -class FrequencyCardTest : BaseViewTest() { +class FrequencyCardViewTest : BaseViewTest() { val PATH = "habits/show/FrequencyCard/" - private lateinit var view: FrequencyCard + private lateinit var view: FrequencyCardView @Before override fun setUp() { @@ -41,8 +42,8 @@ class FrequencyCardTest : BaseViewTest() { view = LayoutInflater .from(targetContext) .inflate(R.layout.show_habit, null) - .findViewById(R.id.frequencyCard) as FrequencyCard - view.update(FrequencyCardPresenter(habit, 0).present()) + .findViewById(R.id.frequencyCard) as FrequencyCardView + view.update(FrequencyCardPresenter().present(habit = habit, firstWeekday = 0)) measureView(view, 800f, 600f) } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardViewTest.kt similarity index 88% rename from android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardTest.kt rename to android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardViewTest.kt index 040675c94..b39fb51dd 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardViewTest.kt @@ -24,14 +24,15 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.R +import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardPresenter import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @MediumTest -class HistoryCardTest : BaseViewTest() { - private lateinit var view: HistoryCard +class HistoryCardViewTest : BaseViewTest() { + private lateinit var view: HistoryCardView val PATH = "habits/show/HistoryCard/" @Before @@ -41,13 +42,13 @@ class HistoryCardTest : BaseViewTest() { view = LayoutInflater .from(targetContext) .inflate(R.layout.show_habit, null) - .findViewById(R.id.historyCard) as HistoryCard + .findViewById(R.id.historyCard) as HistoryCardView view.update( - HistoryCardPresenter( + HistoryCardPresenter().present( habit = habit, firstWeekday = 1, isSkipEnabled = false - ).present() + ) ) measureView(view, 800f, 600f) } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardViewTest.kt index 8ee1e67e2..f7e274ba6 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardViewTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardViewTest.kt @@ -25,6 +25,7 @@ import androidx.test.filters.MediumTest import org.hamcrest.Matchers.equalTo import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.R +import org.isoron.uhabits.core.ui.screens.habits.show.views.NotesCardViewModel import org.junit.Assert.assertThat import org.junit.Before import org.junit.Test diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardViewTest.kt index 94de408bf..3bf6edaa4 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardViewTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardViewTest.kt @@ -25,6 +25,7 @@ import androidx.test.filters.MediumTest import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.R import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardViewModel import org.junit.Before import org.junit.Test import org.junit.runner.RunWith diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardViewTest.kt similarity index 78% rename from android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardTest.kt rename to android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardViewTest.kt index 035cf8678..3c1981c53 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardViewTest.kt @@ -24,15 +24,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.R +import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @MediumTest -class ScoreCardTest : BaseViewTest() { +class ScoreCardViewTest : BaseViewTest() { val PATH = "habits/show/ScoreCard/" - private lateinit var view: ScoreCard + private lateinit var view: ScoreCardView @Before override fun setUp() { @@ -41,8 +42,14 @@ class ScoreCardTest : BaseViewTest() { view = LayoutInflater .from(targetContext) .inflate(R.layout.show_habit, null) - .findViewById(R.id.scoreCard) as ScoreCard - view.update(ScoreCardPresenter(habit, 0).present(0)) + .findViewById(R.id.scoreCard) as ScoreCardView + view.update( + ScoreCardPresenter().present( + habit = habit, + firstWeekday = 0, + spinnerPosition = 0, + ) + ) measureView(view, 800f, 600f) } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/StreakCardViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/StreakCardViewTest.kt index 5321438e6..2968ccfab 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/StreakCardViewTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/StreakCardViewTest.kt @@ -24,6 +24,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.R +import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardViewModel import org.junit.Before import org.junit.Test import org.junit.runner.RunWith diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardViewTest.kt index 44e425017..076e226c8 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardViewTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardViewTest.kt @@ -23,7 +23,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.R +import org.isoron.uhabits.core.models.Frequency import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.Reminder +import org.isoron.uhabits.core.models.WeekdayList.EVERY_DAY +import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardViewModel import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -44,11 +48,12 @@ class SubtitleCardViewTest : BaseViewTest() { view.update( SubtitleCardViewModel( color = PaletteColor(7), - frequencyText = "3 times in 7 days", + frequency = Frequency(3, 7), isNumerical = false, question = "Did you meditate this morning?", - reminderText = "8:30 AM", - targetText = "", + reminder = Reminder(8, 30, EVERY_DAY), + unit = "", + targetValue = 0.0, ) ) measureView(view, 800f, 200f) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java index 72fad14b1..8996a9d9a 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java @@ -31,12 +31,12 @@ import android.util.*; import org.isoron.uhabits.*; import org.isoron.uhabits.activities.common.views.*; -import org.isoron.uhabits.activities.habits.show.views.*; import org.isoron.uhabits.core.commands.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.preferences.*; import org.isoron.uhabits.core.tasks.*; import org.isoron.uhabits.core.ui.callbacks.*; +import org.isoron.uhabits.core.ui.screens.habits.show.views.*; import org.isoron.uhabits.utils.*; import org.jetbrains.annotations.*; @@ -179,11 +179,11 @@ public class HistoryEditorDialog extends AppCompatDialogFragment @Override public void doInBackground() { - HistoryCardViewModel model = new HistoryCardPresenter( + HistoryCardViewModel model = new HistoryCardPresenter().present( habit, prefs.getFirstWeekday(), prefs.isSkipEnabled() - ).present(); + ); checkmarks = model.getEntries(); } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt index bd1af8215..9b22bdfef 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt @@ -34,16 +34,22 @@ import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialogFactory import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory import org.isoron.uhabits.core.commands.Command import org.isoron.uhabits.core.commands.CommandRunner +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitBehavior import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuBehavior +import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitPresenter import org.isoron.uhabits.intents.IntentFactory class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { + private val presenter = ShowHabitPresenter() + private lateinit var commandRunner: CommandRunner private lateinit var menu: ShowHabitMenu - private lateinit var presenter: ShowHabitPresenter private lateinit var view: ShowHabitView + private lateinit var habit: Habit + private lateinit var preferences: Preferences private val scope = CoroutineScope(Dispatchers.Main) @@ -52,17 +58,12 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { val appComponent = (applicationContext as HabitsApplication).component val habitList = appComponent.habitList - val habit = habitList.getById(ContentUris.parseId(intent.data!!))!! - val preferences = appComponent.preferences + habit = habitList.getById(ContentUris.parseId(intent.data!!))!! + preferences = appComponent.preferences commandRunner = appComponent.commandRunner AndroidThemeSwitcher(this, preferences).apply() view = ShowHabitView(this) - presenter = ShowHabitPresenter( - context = this, - habit = habit, - preferences = appComponent.preferences, - ) val screen = ShowHabitScreen( activity = this, @@ -129,7 +130,12 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { fun refresh() { scope.launch { - view.update(presenter.present()) + view.update( + presenter.present( + habit = habit, + preferences = preferences, + ) + ) } } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitView.kt index 562215dc8..3b84db82f 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitView.kt @@ -22,45 +22,10 @@ package org.isoron.uhabits.activities.habits.show import android.content.Context import android.view.LayoutInflater import android.widget.FrameLayout -import org.isoron.uhabits.activities.habits.show.views.BarCardPresenter -import org.isoron.uhabits.activities.habits.show.views.BarCardViewModel -import org.isoron.uhabits.activities.habits.show.views.FrequencyCardPresenter -import org.isoron.uhabits.activities.habits.show.views.FrequencyCardViewModel -import org.isoron.uhabits.activities.habits.show.views.HistoryCardPresenter -import org.isoron.uhabits.activities.habits.show.views.HistoryCardViewModel -import org.isoron.uhabits.activities.habits.show.views.NotesCardPresenter -import org.isoron.uhabits.activities.habits.show.views.NotesCardViewModel -import org.isoron.uhabits.activities.habits.show.views.OverviewCardPresenter -import org.isoron.uhabits.activities.habits.show.views.OverviewCardViewModel -import org.isoron.uhabits.activities.habits.show.views.ScoreCardPresenter -import org.isoron.uhabits.activities.habits.show.views.ScoreCardViewModel -import org.isoron.uhabits.activities.habits.show.views.StreakCardViewModel -import org.isoron.uhabits.activities.habits.show.views.StreakCartPresenter -import org.isoron.uhabits.activities.habits.show.views.SubtitleCardPresenter -import org.isoron.uhabits.activities.habits.show.views.SubtitleCardViewModel -import org.isoron.uhabits.activities.habits.show.views.TargetCardPresenter -import org.isoron.uhabits.activities.habits.show.views.TargetCardViewModel -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.preferences.Preferences +import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitViewModel import org.isoron.uhabits.databinding.ShowHabitBinding import org.isoron.uhabits.utils.setupToolbar -data class ShowHabitViewModel( - val title: String = "", - val isNumerical: Boolean = false, - val color: PaletteColor = PaletteColor(1), - val subtitle: SubtitleCardViewModel, - val overview: OverviewCardViewModel, - val notes: NotesCardViewModel, - val target: TargetCardViewModel, - val streaks: StreakCardViewModel, - val scores: ScoreCardViewModel, - val frequency: FrequencyCardViewModel, - val history: HistoryCardViewModel, - val bar: BarCardViewModel, -) - class ShowHabitView(context: Context) : FrameLayout(context) { private val binding = ShowHabitBinding.inflate(LayoutInflater.from(context)) @@ -96,58 +61,3 @@ class ShowHabitView(context: Context) : FrameLayout(context) { } } } - -class ShowHabitPresenter( - val habit: Habit, - val context: Context, - val preferences: Preferences, -) { - private val subtitleCardPresenter = SubtitleCardPresenter(habit, context) - private val overviewCardPresenter = OverviewCardPresenter(habit) - private val notesCardPresenter = NotesCardPresenter(habit) - private val targetCardPresenter = TargetCardPresenter( - habit = habit, - firstWeekday = preferences.firstWeekday, - resources = context.resources, - ) - private val streakCartPresenter = StreakCartPresenter(habit) - private val scoreCardPresenter = ScoreCardPresenter( - habit = habit, - firstWeekday = preferences.firstWeekday, - ) - private val frequencyCardPresenter = FrequencyCardPresenter( - habit = habit, - firstWeekday = preferences.firstWeekday, - ) - private val historyCardViewModel = HistoryCardPresenter( - habit = habit, - firstWeekday = preferences.firstWeekday, - isSkipEnabled = preferences.isSkipEnabled, - ) - private val barCardPresenter = BarCardPresenter( - habit = habit, - firstWeekday = preferences.firstWeekday, - ) - - suspend fun present(): ShowHabitViewModel { - return ShowHabitViewModel( - title = habit.name, - color = habit.color, - isNumerical = habit.isNumerical, - subtitle = subtitleCardPresenter.present(), - overview = overviewCardPresenter.present(), - notes = notesCardPresenter.present(), - target = targetCardPresenter.present(), - streaks = streakCartPresenter.present(), - scores = scoreCardPresenter.present( - spinnerPosition = preferences.scoreCardSpinnerPosition - ), - frequency = frequencyCardPresenter.present(), - history = historyCardViewModel.present(), - bar = barCardPresenter.present( - boolSpinnerPosition = preferences.barCardBoolSpinnerPosition, - numericalSpinnerPosition = preferences.barCardNumericalSpinnerPosition, - ), - ) - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCardView.kt similarity index 62% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.kt rename to android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCardView.kt index 813244a60..44c7a40f2 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCardView.kt @@ -24,25 +24,11 @@ import android.view.LayoutInflater import android.view.View import android.widget.AdapterView import android.widget.LinearLayout -import org.isoron.uhabits.activities.habits.show.views.ScoreCardPresenter.Companion.getTruncateField -import org.isoron.uhabits.core.models.Entry -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.models.groupedSum -import org.isoron.uhabits.core.utils.DateUtils +import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardViewModel import org.isoron.uhabits.databinding.ShowHabitBarBinding import org.isoron.uhabits.utils.toThemedAndroidColor -data class BarCardViewModel( - val entries: List, - val bucketSize: Int, - val color: PaletteColor, - val isNumerical: Boolean, - val numericalSpinnerPosition: Int, - val boolSpinnerPosition: Int, -) - -class BarCard(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { +class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { private var binding = ShowHabitBarBinding.inflate(LayoutInflater.from(context), this) var onNumericalSpinnerPosition: (position: Int) -> Unit = {} @@ -94,37 +80,3 @@ class BarCard(context: Context, attrs: AttributeSet) : LinearLayout(context, att } } } - -class BarCardPresenter( - val habit: Habit, - val firstWeekday: Int, -) { - val numericalBucketSizes = intArrayOf(1, 7, 31, 92, 365) - val boolBucketSizes = intArrayOf(7, 31, 92, 365) - - fun present( - numericalSpinnerPosition: Int, - boolSpinnerPosition: Int, - ): BarCardViewModel { - val bucketSize = if (habit.isNumerical) { - numericalBucketSizes[numericalSpinnerPosition] - } else { - boolBucketSizes[boolSpinnerPosition] - } - val today = DateUtils.getToday() - val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today - val entries = habit.computedEntries.getByInterval(oldest, today).groupedSum( - truncateField = getTruncateField(bucketSize), - firstWeekday = firstWeekday, - isNumerical = habit.isNumerical, - ) - return BarCardViewModel( - entries = entries, - bucketSize = bucketSize, - color = habit.color, - isNumerical = habit.isNumerical, - numericalSpinnerPosition = numericalSpinnerPosition, - boolSpinnerPosition = boolSpinnerPosition, - ) - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardView.kt similarity index 67% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCard.kt rename to android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardView.kt index fb9719672..1e2568f10 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardView.kt @@ -22,20 +22,11 @@ import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.models.Timestamp +import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardViewModel import org.isoron.uhabits.databinding.ShowHabitFrequencyBinding import org.isoron.uhabits.utils.toThemedAndroidColor -import java.util.HashMap -data class FrequencyCardViewModel( - val frequency: HashMap>, - val firstWeekday: Int, - val color: PaletteColor, -) - -class FrequencyCard(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { +class FrequencyCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { private var binding = ShowHabitFrequencyBinding.inflate(LayoutInflater.from(context), this) @@ -47,16 +38,3 @@ class FrequencyCard(context: Context, attrs: AttributeSet) : LinearLayout(contex binding.frequencyChart.setColor(androidColor) } } - -class FrequencyCardPresenter( - val habit: Habit, - val firstWeekday: Int, -) { - fun present() = FrequencyCardViewModel( - color = habit.color, - frequency = habit.originalEntries.computeWeekdayFrequency( - isNumerical = habit.isNumerical - ), - firstWeekday = firstWeekday, - ) -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardView.kt similarity index 63% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.kt rename to android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardView.kt index 2627d9eda..8c6b55471 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardView.kt @@ -22,21 +22,11 @@ import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.utils.DateUtils +import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardViewModel import org.isoron.uhabits.databinding.ShowHabitHistoryBinding import org.isoron.uhabits.utils.toThemedAndroidColor -data class HistoryCardViewModel( - val entries: IntArray, - val color: PaletteColor, - val firstWeekday: Int, - val isNumerical: Boolean, - val isSkipEnabled: Boolean, -) - -class HistoryCard(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { +class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { private var binding = ShowHabitHistoryBinding.inflate(LayoutInflater.from(context), this) @@ -58,23 +48,3 @@ class HistoryCard(context: Context, attrs: AttributeSet) : LinearLayout(context, } } } - -class HistoryCardPresenter( - val habit: Habit, - val firstWeekday: Int, - val isSkipEnabled: Boolean, -) { - fun present(): HistoryCardViewModel { - val today = DateUtils.getTodayWithOffset() - val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today - val entries = habit.computedEntries.getByInterval(oldest, today).map { it.value }.toIntArray() - - return HistoryCardViewModel( - entries = entries, - color = habit.color, - firstWeekday = firstWeekday, - isNumerical = habit.isNumerical, - isSkipEnabled = isSkipEnabled, - ) - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCardView.kt similarity index 86% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCard.kt rename to android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCardView.kt index 50e51d438..788f92c75 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCardView.kt @@ -23,11 +23,9 @@ import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout -import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.ui.screens.habits.show.views.NotesCardViewModel import org.isoron.uhabits.databinding.ShowHabitNotesBinding -data class NotesCardViewModel(val description: String) - class NotesCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { private val binding = ShowHabitNotesBinding.inflate(LayoutInflater.from(context), this) fun update(data: NotesCardViewModel) { @@ -40,9 +38,3 @@ class NotesCardView(context: Context, attrs: AttributeSet) : LinearLayout(contex invalidate() } } - -class NotesCardPresenter(val habit: Habit) { - fun present() = NotesCardViewModel( - description = habit.description, - ) -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardView.kt similarity index 65% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.kt rename to android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardView.kt index cb3a8fce9..d57fe84ed 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardView.kt @@ -22,25 +22,12 @@ import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.invoke import org.isoron.uhabits.R -import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.utils.DateUtils +import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardViewModel import org.isoron.uhabits.databinding.ShowHabitOverviewBinding import org.isoron.uhabits.utils.StyledResources import org.isoron.uhabits.utils.toThemedAndroidColor -data class OverviewCardViewModel( - val color: PaletteColor, - val scoreMonthDiff: Float, - val scoreYearDiff: Float, - val scoreToday: Float, - val totalCount: Long, -) - class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { private val binding = ShowHabitOverviewBinding.inflate(LayoutInflater.from(context), this) @@ -71,26 +58,3 @@ class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(con postInvalidate() } } - -class OverviewCardPresenter(val habit: Habit) { - suspend fun present(): OverviewCardViewModel = Dispatchers.IO { - val today = DateUtils.getTodayWithOffset() - val lastMonth = today.minus(30) - val lastYear = today.minus(365) - val scores = habit.scores - val scoreToday = scores.get(today).value.toFloat() - val scoreLastMonth = scores.get(lastMonth).value.toFloat() - val scoreLastYear = scores.get(lastYear).value.toFloat() - val totalCount = habit.originalEntries.getKnown() - .filter { it.value == YES_MANUAL } - .count() - .toLong() - return@IO OverviewCardViewModel( - color = habit.color, - scoreToday = scoreToday, - scoreMonthDiff = scoreToday - scoreLastMonth, - scoreYearDiff = scoreToday - scoreLastYear, - totalCount = totalCount, - ) - } -} 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 deleted file mode 100644 index 2dcdc9d08..000000000 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2016 Álinson Santos Xavier - * - * 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 . - */ -package org.isoron.uhabits.activities.habits.show.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.widget.AdapterView -import android.widget.LinearLayout -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.models.Score -import org.isoron.uhabits.core.utils.DateUtils -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.DAY -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.MONTH -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.QUARTER -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.WEEK_NUMBER -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.YEAR -import org.isoron.uhabits.databinding.ShowHabitScoreBinding -import org.isoron.uhabits.utils.toThemedAndroidColor - -data class ScoreCardViewModel( - val scores: List, - val bucketSize: Int, - val spinnerPosition: Int, - val color: PaletteColor, -) - -class ScoreCard(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { - private var binding = ShowHabitScoreBinding.inflate(LayoutInflater.from(context), this) - - var onSpinnerPosition: (position: Int) -> Unit = {} - - fun update(data: ScoreCardViewModel) { - val androidColor = data.color.toThemedAndroidColor(context) - binding.title.setTextColor(androidColor) - binding.spinner.setSelection(data.spinnerPosition) - binding.scoreView.setScores(data.scores) - binding.scoreView.reset() - binding.scoreView.setBucketSize(data.bucketSize) - binding.scoreView.setColor(androidColor) - - binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - onSpinnerPosition(position) - } - override fun onNothingSelected(parent: AdapterView<*>?) { - } - } - } -} - -class ScoreCardPresenter( - val habit: Habit, - val firstWeekday: Int, -) { - companion object { - 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 - 365 -> return YEAR - else -> return MONTH - } - } - } - - fun present(spinnerPosition: Int): ScoreCardViewModel { - val bucketSize = BUCKET_SIZES[spinnerPosition] - val today = DateUtils.getTodayWithOffset() - val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today - - 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, - bucketSize = bucketSize, - spinnerPosition = spinnerPosition, - ) - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardView.kt new file mode 100644 index 000000000..0bc74b1d7 --- /dev/null +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardView.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ +package org.isoron.uhabits.activities.habits.show.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.AdapterView +import android.widget.LinearLayout +import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardViewModel +import org.isoron.uhabits.databinding.ShowHabitScoreBinding +import org.isoron.uhabits.utils.toThemedAndroidColor + +class ScoreCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { + private var binding = ShowHabitScoreBinding.inflate(LayoutInflater.from(context), this) + + var onSpinnerPosition: (position: Int) -> Unit = {} + + fun update(data: ScoreCardViewModel) { + val androidColor = data.color.toThemedAndroidColor(context) + binding.title.setTextColor(androidColor) + binding.spinner.setSelection(data.spinnerPosition) + binding.scoreView.setScores(data.scores) + binding.scoreView.reset() + binding.scoreView.setBucketSize(data.bucketSize) + binding.scoreView.setColor(androidColor) + + binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + onSpinnerPosition(position) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + } + } + } +} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCardView.kt similarity index 73% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCard.kt rename to android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCardView.kt index 661cc8484..baa18f0fa 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCardView.kt @@ -22,19 +22,10 @@ import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.invoke -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.PaletteColor -import org.isoron.uhabits.core.models.Streak +import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardViewModel import org.isoron.uhabits.databinding.ShowHabitStreakBinding import org.isoron.uhabits.utils.toThemedAndroidColor -data class StreakCardViewModel( - val color: PaletteColor, - val bestStreaks: List -) - class StreakCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { private val binding = ShowHabitStreakBinding.inflate(LayoutInflater.from(context), this) fun update(data: StreakCardViewModel) { @@ -45,12 +36,3 @@ class StreakCardView(context: Context, attrs: AttributeSet) : LinearLayout(conte postInvalidate() } } - -class StreakCartPresenter(val habit: Habit) { - suspend fun present(): StreakCardViewModel = Dispatchers.IO { - return@IO StreakCardViewModel( - color = habit.color, - bestStreaks = habit.streaks.getBest(10), - ) - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardView.kt similarity index 76% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.kt rename to android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardView.kt index 0e70c3e4f..2cc143fa0 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardView.kt @@ -28,23 +28,13 @@ import android.widget.LinearLayout import org.isoron.uhabits.R import org.isoron.uhabits.activities.habits.list.views.toShortString import org.isoron.uhabits.core.models.Frequency -import org.isoron.uhabits.core.models.Habit -import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardViewModel import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding import org.isoron.uhabits.utils.InterfaceUtils import org.isoron.uhabits.utils.formatTime import org.isoron.uhabits.utils.toThemedAndroidColor import java.util.Locale -data class SubtitleCardViewModel( - val color: PaletteColor, - val frequencyText: String, - val isNumerical: Boolean, - val question: String, - val reminderText: String, - val targetText: String, -) - class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { private val binding = ShowHabitSubtitleBinding.inflate(LayoutInflater.from(context), this) @@ -56,13 +46,19 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con binding.reminderIcon.typeface = fontAwesome } + @SuppressLint("SetTextI18n") fun update(data: SubtitleCardViewModel) { val color = data.color.toThemedAndroidColor(context) - binding.frequencyLabel.text = data.frequencyText + val reminder = data.reminder + binding.frequencyLabel.text = data.frequency.format(resources) binding.questionLabel.setTextColor(color) binding.questionLabel.text = data.question - binding.reminderLabel.text = data.reminderText - binding.targetText.text = data.targetText + binding.reminderLabel.text = if (reminder != null) { + formatTime(context, reminder.hour, reminder.minute) + } else { + resources.getString(R.string.reminder_off) + } + binding.targetText.text = "${data.targetValue.toShortString()} ${data.unit}" binding.questionLabel.visibility = View.VISIBLE binding.targetIcon.visibility = View.VISIBLE @@ -77,32 +73,9 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con postInvalidate() } -} - -class SubtitleCardPresenter( - val habit: Habit, - val context: Context, -) { - val resources: Resources = context.resources - - fun present(): SubtitleCardViewModel { - val reminderText = if (habit.hasReminder()) { - formatTime(context, habit.reminder!!.hour, habit.reminder!!.minute)!! - } else { - resources.getString(R.string.reminder_off) - } - return SubtitleCardViewModel( - color = habit.color, - frequencyText = habit.frequency.format(), - isNumerical = habit.isNumerical, - question = habit.question, - reminderText = reminderText, - targetText = "${habit.targetValue.toShortString()} ${habit.unit}", - ) - } @SuppressLint("StringFormatMatches") - private fun Frequency.format(): String { + private fun Frequency.format(resources: Resources): String { val num = this.numerator val den = this.denominator if (num == den) { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCardView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCardView.kt new file mode 100644 index 000000000..2dd02022b --- /dev/null +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCardView.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ +package org.isoron.uhabits.activities.habits.show.views + +import android.content.Context +import android.content.res.Resources +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.LinearLayout +import org.isoron.uhabits.R +import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardViewModel +import org.isoron.uhabits.databinding.ShowHabitTargetBinding +import org.isoron.uhabits.utils.toThemedAndroidColor + +class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { + private val binding = ShowHabitTargetBinding.inflate(LayoutInflater.from(context), this) + fun update(data: TargetCardViewModel) { + val androidColor = data.color.toThemedAndroidColor(context) + binding.targetChart.setValues(data.values) + binding.targetChart.setTargets(data.targets) + binding.targetChart.setLabels(data.intervals.map { intervalToLabel(resources, it) }) + binding.title.setTextColor(androidColor) + binding.targetChart.setColor(androidColor) + postInvalidate() + } + + companion object { + fun intervalToLabel(resources: Resources, interval: Int) = when (interval) { + 1 -> resources.getString(R.string.today) + 7 -> resources.getString(R.string.week) + 30 -> resources.getString(R.string.month) + 91 -> resources.getString(R.string.quarter) + else -> resources.getString(R.string.year) + } + } +} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt index eeace136e..d1393d5be 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt @@ -23,8 +23,8 @@ import android.app.PendingIntent import android.content.Context import android.view.View import org.isoron.uhabits.activities.common.views.HistoryChart -import org.isoron.uhabits.activities.habits.show.views.HistoryCardPresenter import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardPresenter import org.isoron.uhabits.utils.toThemedAndroidColor import org.isoron.uhabits.widgets.views.GraphWidgetView @@ -43,11 +43,11 @@ class HistoryWidget( val widgetView = view as GraphWidgetView widgetView.setBackgroundAlpha(preferedBackgroundAlpha) if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) - val model = HistoryCardPresenter( + val model = HistoryCardPresenter().present( habit = habit, isSkipEnabled = prefs.isSkipEnabled, firstWeekday = prefs.firstWeekday, - ).present() + ) (widgetView.dataView as HistoryChart).apply { setFirstWeekday(model.firstWeekday) setSkipEnabled(model.isSkipEnabled) 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 edac3d118..cb5a74889 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 @@ -22,8 +22,8 @@ package org.isoron.uhabits.widgets import android.content.Context import android.view.View import org.isoron.uhabits.activities.common.views.ScoreChart -import org.isoron.uhabits.activities.habits.show.views.ScoreCardPresenter import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter import org.isoron.uhabits.utils.toThemedAndroidColor import org.isoron.uhabits.widgets.views.GraphWidgetView @@ -37,8 +37,12 @@ class ScoreWidget( pendingIntentFactory.showHabit(habit) override fun refreshData(view: View) { - val presenter = ScoreCardPresenter(habit, prefs.firstWeekday) - val viewModel = presenter.present(prefs.scoreCardSpinnerPosition) + val presenter = ScoreCardPresenter() + val viewModel = presenter.present( + habit = habit, + firstWeekday = prefs.firstWeekday, + spinnerPosition = prefs.scoreCardSpinnerPosition + ) val widgetView = view as GraphWidgetView widgetView.setBackgroundAlpha(preferedBackgroundAlpha) if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/TargetWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/TargetWidget.kt index 3b3f7a53d..b097785a1 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/TargetWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/TargetWidget.kt @@ -25,8 +25,9 @@ import android.view.ViewGroup.LayoutParams import android.view.ViewGroup.LayoutParams.MATCH_PARENT import kotlinx.coroutines.runBlocking import org.isoron.uhabits.activities.common.views.TargetChart -import org.isoron.uhabits.activities.habits.show.views.TargetCardPresenter +import org.isoron.uhabits.activities.habits.show.views.TargetCardView.Companion.intervalToLabel import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardPresenter import org.isoron.uhabits.utils.toThemedAndroidColor import org.isoron.uhabits.widgets.views.GraphWidgetView @@ -44,11 +45,11 @@ class TargetWidget( widgetView.setBackgroundAlpha(preferedBackgroundAlpha) if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) val chart = (widgetView.dataView as TargetChart) - val presenter = TargetCardPresenter(habit, prefs.firstWeekday, context.resources) - val data = presenter.present() + val presenter = TargetCardPresenter() + val data = presenter.present(habit, prefs.firstWeekday) chart.setColor(data.color.toThemedAndroidColor(context)) chart.setTargets(data.targets) - chart.setLabels(data.labels) + chart.setLabels(data.intervals.map { intervalToLabel(context.resources, it) }) chart.setValues(data.values) } diff --git a/android/uhabits-android/src/main/res/layout/show_habit.xml b/android/uhabits-android/src/main/res/layout/show_habit.xml index 401921532..e2edc87a4 100644 --- a/android/uhabits-android/src/main/res/layout/show_habit.xml +++ b/android/uhabits-android/src/main/res/layout/show_habit.xml @@ -62,17 +62,17 @@ style="@style/Card" android:paddingTop="12dp"/> - - - - diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabit.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabit.kt new file mode 100644 index 000000000..72cce2936 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabit.kt @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show + +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.preferences.Preferences +import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardViewModel +import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardViewModel +import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardViewModel +import org.isoron.uhabits.core.ui.screens.habits.show.views.NotesCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.NotesCardViewModel +import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardViewModel +import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardViewModel +import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardViewModel +import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCartPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardViewModel +import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardPresenter +import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardViewModel + +data class ShowHabitViewModel( + val title: String = "", + val isNumerical: Boolean = false, + val color: PaletteColor = PaletteColor(1), + val subtitle: SubtitleCardViewModel, + val overview: OverviewCardViewModel, + val notes: NotesCardViewModel, + val target: TargetCardViewModel, + val streaks: StreakCardViewModel, + val scores: ScoreCardViewModel, + val frequency: FrequencyCardViewModel, + val history: HistoryCardViewModel, + val bar: BarCardViewModel, +) + +class ShowHabitPresenter { + fun present( + habit: Habit, + preferences: Preferences, + ): ShowHabitViewModel { + return ShowHabitViewModel( + title = habit.name, + color = habit.color, + isNumerical = habit.isNumerical, + subtitle = SubtitleCardPresenter().present( + habit = habit, + ), + overview = OverviewCardPresenter().present( + habit = habit, + ), + notes = NotesCardPresenter().present( + habit = habit, + ), + target = TargetCardPresenter().present( + habit = habit, + firstWeekday = preferences.firstWeekday, + ), + streaks = StreakCartPresenter().present( + habit = habit, + ), + scores = ScoreCardPresenter().present( + spinnerPosition = preferences.scoreCardSpinnerPosition, + habit = habit, + firstWeekday = preferences.firstWeekday, + ), + frequency = FrequencyCardPresenter().present( + habit = habit, + firstWeekday = preferences.firstWeekday, + ), + history = HistoryCardPresenter().present( + habit = habit, + firstWeekday = preferences.firstWeekday, + isSkipEnabled = preferences.isSkipEnabled, + ), + bar = BarCardPresenter().present( + habit = habit, + firstWeekday = preferences.firstWeekday, + boolSpinnerPosition = preferences.barCardBoolSpinnerPosition, + numericalSpinnerPosition = preferences.barCardNumericalSpinnerPosition, + ), + ) + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/BarCard.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/BarCard.kt new file mode 100644 index 000000000..e91ded06f --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/BarCard.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show.views + +import org.isoron.uhabits.core.models.Entry +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.groupedSum +import org.isoron.uhabits.core.utils.DateUtils + +data class BarCardViewModel( + val boolSpinnerPosition: Int, + val bucketSize: Int, + val color: PaletteColor, + val entries: List, + val isNumerical: Boolean, + val numericalSpinnerPosition: Int, +) + +class BarCardPresenter { + val numericalBucketSizes = intArrayOf(1, 7, 31, 92, 365) + val boolBucketSizes = intArrayOf(7, 31, 92, 365) + + fun present( + habit: Habit, + firstWeekday: Int, + numericalSpinnerPosition: Int, + boolSpinnerPosition: Int, + ): BarCardViewModel { + val bucketSize = if (habit.isNumerical) { + numericalBucketSizes[numericalSpinnerPosition] + } else { + boolBucketSizes[boolSpinnerPosition] + } + val today = DateUtils.getToday() + val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today + val entries = habit.computedEntries.getByInterval(oldest, today).groupedSum( + truncateField = ScoreCardPresenter.getTruncateField(bucketSize), + firstWeekday = firstWeekday, + isNumerical = habit.isNumerical, + ) + return BarCardViewModel( + entries = entries, + bucketSize = bucketSize, + color = habit.color, + isNumerical = habit.isNumerical, + numericalSpinnerPosition = numericalSpinnerPosition, + boolSpinnerPosition = boolSpinnerPosition, + ) + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/FrequencyCard.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/FrequencyCard.kt new file mode 100644 index 000000000..85e319a24 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/FrequencyCard.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show.views + +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.Timestamp +import java.util.HashMap + +data class FrequencyCardViewModel( + val color: PaletteColor, + val firstWeekday: Int, + val frequency: HashMap>, +) + +class FrequencyCardPresenter { + fun present( + habit: Habit, + firstWeekday: Int, + ) = FrequencyCardViewModel( + color = habit.color, + frequency = habit.originalEntries.computeWeekdayFrequency( + isNumerical = habit.isNumerical + ), + firstWeekday = firstWeekday, + ) +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt new file mode 100644 index 000000000..94b8040c9 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/HistoryCard.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show.views + +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.utils.DateUtils + +data class HistoryCardViewModel( + val color: PaletteColor, + val entries: IntArray, + val firstWeekday: Int, + val isNumerical: Boolean, + val isSkipEnabled: Boolean, +) + +class HistoryCardPresenter { + fun present( + habit: Habit, + firstWeekday: Int, + isSkipEnabled: Boolean, + ): HistoryCardViewModel { + val today = DateUtils.getTodayWithOffset() + val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today + val entries = + habit.computedEntries.getByInterval(oldest, today).map { it.value }.toIntArray() + + return HistoryCardViewModel( + entries = entries, + color = habit.color, + firstWeekday = firstWeekday, + isNumerical = habit.isNumerical, + isSkipEnabled = isSkipEnabled, + ) + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/NotesCard.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/NotesCard.kt new file mode 100644 index 000000000..b210f7ec7 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/NotesCard.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show.views + +import org.isoron.uhabits.core.models.Habit + +data class NotesCardViewModel( + val description: String, +) + +class NotesCardPresenter { + fun present(habit: Habit) = NotesCardViewModel( + description = habit.description, + ) +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/OverviewCard.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/OverviewCard.kt new file mode 100644 index 000000000..d37c520c4 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/OverviewCard.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show.views + +import org.isoron.uhabits.core.models.Entry +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.utils.DateUtils + +data class OverviewCardViewModel( + val color: PaletteColor, + val scoreMonthDiff: Float, + val scoreYearDiff: Float, + val scoreToday: Float, + val totalCount: Long, +) + +class OverviewCardPresenter { + fun present(habit: Habit): OverviewCardViewModel { + val today = DateUtils.getTodayWithOffset() + val lastMonth = today.minus(30) + val lastYear = today.minus(365) + val scores = habit.scores + val scoreToday = scores.get(today).value.toFloat() + val scoreLastMonth = scores.get(lastMonth).value.toFloat() + val scoreLastYear = scores.get(lastYear).value.toFloat() + val totalCount = habit.originalEntries.getKnown() + .filter { it.value == Entry.YES_MANUAL } + .count() + .toLong() + return OverviewCardViewModel( + color = habit.color, + scoreToday = scoreToday, + scoreMonthDiff = scoreToday - scoreLastMonth, + scoreYearDiff = scoreToday - scoreLastYear, + totalCount = totalCount, + ) + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/ScoreCard.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/ScoreCard.kt new file mode 100644 index 000000000..5813e7c28 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/ScoreCard.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show.views + +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.Score +import org.isoron.uhabits.core.utils.DateUtils + +data class ScoreCardViewModel( + val scores: List, + val bucketSize: Int, + val spinnerPosition: Int, + val color: PaletteColor, +) + +class ScoreCardPresenter { + companion object { + val BUCKET_SIZES = intArrayOf(1, 7, 31, 92, 365) + fun getTruncateField(bucketSize: Int): DateUtils.TruncateField { + when (bucketSize) { + 1 -> return DateUtils.TruncateField.DAY + 7 -> return DateUtils.TruncateField.WEEK_NUMBER + 31 -> return DateUtils.TruncateField.MONTH + 92 -> return DateUtils.TruncateField.QUARTER + 365 -> return DateUtils.TruncateField.YEAR + else -> return DateUtils.TruncateField.MONTH + } + } + } + + fun present( + habit: Habit, + firstWeekday: Int, + spinnerPosition: Int, + ): ScoreCardViewModel { + val bucketSize = BUCKET_SIZES[spinnerPosition] + val today = DateUtils.getTodayWithOffset() + val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today + + 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, + bucketSize = bucketSize, + spinnerPosition = spinnerPosition, + ) + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/StreakCart.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/StreakCart.kt new file mode 100644 index 000000000..381da43ee --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/StreakCart.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show.views + +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.Streak + +data class StreakCardViewModel( + val color: PaletteColor, + val bestStreaks: List +) + +class StreakCartPresenter { + fun present(habit: Habit): StreakCardViewModel { + return StreakCardViewModel( + color = habit.color, + bestStreaks = habit.streaks.getBest(10), + ) + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt new file mode 100644 index 000000000..f39d78fae --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.show.views + +import org.isoron.uhabits.core.models.Frequency +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.Reminder + +data class SubtitleCardViewModel( + val color: PaletteColor, + val frequency: Frequency, + val isNumerical: Boolean, + val question: String, + val reminder: Reminder?, + val targetValue: Double, + val unit: String, +) + +class SubtitleCardPresenter { + fun present( + habit: Habit, + ): SubtitleCardViewModel = SubtitleCardViewModel( + color = habit.color, + frequency = habit.frequency, + isNumerical = habit.isNumerical, + question = habit.question, + reminder = habit.reminder, + targetValue = habit.targetValue, + unit = habit.unit, + ) +} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt similarity index 58% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.kt rename to android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt index 4add0f185..6e6ac81c1 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.kt +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2016-2020 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -16,27 +16,13 @@ * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ -package org.isoron.uhabits.activities.habits.show.views -import android.content.Context -import android.content.res.Resources -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.LinearLayout -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.invoke -import org.isoron.uhabits.R +package org.isoron.uhabits.core.ui.screens.habits.show.views + import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.models.groupedSum import org.isoron.uhabits.core.utils.DateUtils -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.DAY -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.MONTH -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.QUARTER -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.WEEK_NUMBER -import org.isoron.uhabits.core.utils.DateUtils.TruncateField.YEAR -import org.isoron.uhabits.databinding.ShowHabitTargetBinding -import org.isoron.uhabits.utils.toThemedAndroidColor import java.util.ArrayList import java.util.Calendar @@ -44,55 +30,41 @@ data class TargetCardViewModel( val color: PaletteColor, val values: List = listOf(), val targets: List = listOf(), - val labels: List = listOf(), + val intervals: List = listOf(), ) -class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { - private val binding = ShowHabitTargetBinding.inflate(LayoutInflater.from(context), this) - fun update(data: TargetCardViewModel) { - val androidColor = data.color.toThemedAndroidColor(context) - binding.targetChart.setValues(data.values) - binding.targetChart.setTargets(data.targets) - binding.targetChart.setLabels(data.labels) - binding.title.setTextColor(androidColor) - binding.targetChart.setColor(androidColor) - postInvalidate() - } -} - -class TargetCardPresenter( - val habit: Habit, - val firstWeekday: Int, - val resources: Resources, -) { - suspend fun present(): TargetCardViewModel = Dispatchers.IO { +class TargetCardPresenter { + fun present( + habit: Habit, + firstWeekday: Int, + ): TargetCardViewModel { val today = DateUtils.getTodayWithOffset() val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today val entries = habit.computedEntries.getByInterval(oldest, today) val valueToday = entries.groupedSum( - truncateField = DAY, + truncateField = DateUtils.TruncateField.DAY, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 val valueThisWeek = entries.groupedSum( - truncateField = WEEK_NUMBER, + truncateField = DateUtils.TruncateField.WEEK_NUMBER, firstWeekday = firstWeekday, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 val valueThisMonth = entries.groupedSum( - truncateField = MONTH, + truncateField = DateUtils.TruncateField.MONTH, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 val valueThisQuarter = entries.groupedSum( - truncateField = QUARTER, + truncateField = DateUtils.TruncateField.QUARTER, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 val valueThisYear = entries.groupedSum( - truncateField = YEAR, + truncateField = DateUtils.TruncateField.YEAR, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 @@ -121,18 +93,18 @@ class TargetCardPresenter( targets.add(targetThisQuarter) targets.add(targetThisYear) - val labels = ArrayList() - if (habit.frequency.denominator <= 1) labels.add(resources.getString(R.string.today)) - if (habit.frequency.denominator <= 7) labels.add(resources.getString(R.string.week)) - labels.add(resources.getString(R.string.month)) - labels.add(resources.getString(R.string.quarter)) - labels.add(resources.getString(R.string.year)) + val intervals = ArrayList() + if (habit.frequency.denominator <= 1) intervals.add(1) + if (habit.frequency.denominator <= 7) intervals.add(7) + intervals.add(30) + intervals.add(91) + intervals.add(365) - return@IO TargetCardViewModel( + return TargetCardViewModel( color = habit.color, values = values, - labels = labels, targets = targets, + intervals = intervals, ) } } From 6f2b2ab883addff1b0231f9a3e49f4a3eba13e85 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Sun, 3 Jan 2021 11:31:55 -0600 Subject: [PATCH 4/4] Remove Google AutoFactory; expand Dagger imports Fixes #681 --- android/uhabits-android/build.gradle | 4 --- .../common/dialogs/ConfirmDeleteDialog.java | 9 ++--- .../common/dialogs/ConfirmSyncKeyDialog.java | 9 ++--- .../habits/list/ListHabitsScreen.kt | 10 +++--- .../habits/list/views/CheckmarkButtonView.kt | 16 ++++++--- .../habits/list/views/CheckmarkPanelView.kt | 19 ++++++---- .../habits/list/views/HabitCardListView.kt | 22 ++++++++---- .../habits/list/views/HabitCardView.kt | 22 ++++++++---- .../habits/list/views/NumberButtonView.kt | 16 ++++++--- .../habits/list/views/NumberPanelView.kt | 19 ++++++---- .../habits/show/ShowHabitActivity.kt | 2 -- .../activities/habits/show/ShowHabitScreen.kt | 5 ++- .../uhabits/inject/ActivityContextModule.java | 6 ++-- .../uhabits/inject/AppContextModule.java | 5 +-- .../uhabits/tasks/AndroidTaskRunner.java | 3 +- .../isoron/uhabits/tasks/ExportDBTask.java | 10 ++---- .../uhabits/tasks/ExportDBTaskFactory.kt | 33 +++++++++++++++++ .../isoron/uhabits/tasks/ImportDataTask.java | 7 ++-- .../uhabits/tasks/ImportDataTaskFactory.kt | 34 ++++++++++++++++++ android/uhabits-core/build.gradle | 2 -- .../uhabits/core/tasks/ExportCSVTask.java | 5 +-- .../core/tasks/ExportCSVTaskFactory.kt | 36 +++++++++++++++++++ .../core/ui/screens/habits/list/HintList.java | 5 +-- .../ui/screens/habits/list/HintListFactory.kt | 30 ++++++++++++++++ 24 files changed, 239 insertions(+), 90 deletions(-) create mode 100644 android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTaskFactory.kt create mode 100644 android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTaskFactory.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/tasks/ExportCSVTaskFactory.kt create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HintListFactory.kt diff --git a/android/uhabits-android/build.gradle b/android/uhabits-android/build.gradle index 2ba358d08..e644dd89e 100644 --- a/android/uhabits-android/build.gradle +++ b/android/uhabits-android/build.gradle @@ -78,8 +78,6 @@ android { } dependencies { - androidTestAnnotationProcessor "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION" - androidTestCompileOnly "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION" androidTestImplementation "androidx.test.espresso:espresso-contrib:$ESPRESSO_VERSION" androidTestImplementation "androidx.test.espresso:espresso-core:$ESPRESSO_VERSION" androidTestImplementation "com.google.dagger:dagger:$DAGGER_VERSION" @@ -93,8 +91,6 @@ dependencies { androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' androidTestImplementation 'androidx.test:rules:1.3.0' androidTestImplementation project(":uhabits-core") - annotationProcessor "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION" - compileOnly "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION" compileOnly "javax.annotation:jsr250-api:1.0" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1' implementation "com.github.paolorotolo:appintro:3.4.0" diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmDeleteDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmDeleteDialog.java index 5c0fbfe23..be665a7cd 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmDeleteDialog.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmDeleteDialog.java @@ -25,8 +25,6 @@ import android.content.res.*; import androidx.annotation.*; import androidx.appcompat.app.*; -import com.google.auto.factory.*; - import org.isoron.uhabits.R; import org.isoron.uhabits.core.ui.callbacks.*; import org.isoron.uhabits.inject.*; @@ -34,12 +32,11 @@ import org.isoron.uhabits.inject.*; /** * Dialog that asks the user confirmation before executing a delete operation. */ -@AutoFactory(allowSubclasses = true) public class ConfirmDeleteDialog extends AlertDialog { - protected ConfirmDeleteDialog(@Provided @ActivityContext Context context, - @NonNull OnConfirmedCallback callback, - int quantity) + public ConfirmDeleteDialog(@ActivityContext Context context, + @NonNull OnConfirmedCallback callback, + int quantity) { super(context); Resources res = context.getResources(); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmSyncKeyDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmSyncKeyDialog.java index f755046e0..672c7d117 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmSyncKeyDialog.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmSyncKeyDialog.java @@ -25,17 +25,14 @@ import android.content.res.*; import androidx.annotation.*; import androidx.appcompat.app.*; -import com.google.auto.factory.*; - -import org.isoron.uhabits.R; +import org.isoron.uhabits.*; import org.isoron.uhabits.core.ui.callbacks.*; import org.isoron.uhabits.inject.*; -@AutoFactory(allowSubclasses = true) public class ConfirmSyncKeyDialog extends AlertDialog { - protected ConfirmSyncKeyDialog(@Provided @ActivityContext Context context, - @NonNull OnConfirmedCallback callback) + public ConfirmSyncKeyDialog(@ActivityContext Context context, + @NonNull OnConfirmedCallback callback) { super(context); setTitle(R.string.device_sync); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt index 628cd7d8e..133a1c7b3 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt @@ -27,8 +27,8 @@ import androidx.appcompat.app.AppCompatActivity import dagger.Lazy import org.isoron.uhabits.R import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory -import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialogFactory -import org.isoron.uhabits.activities.common.dialogs.ConfirmSyncKeyDialogFactory +import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog +import org.isoron.uhabits.activities.common.dialogs.ConfirmSyncKeyDialog import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter @@ -91,8 +91,6 @@ class ListHabitsScreen private val taskRunner: TaskRunner, private val exportDBFactory: ExportDBTaskFactory, private val importTaskFactory: ImportDataTaskFactory, - private val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory, - private val confirmSyncKeyDialogFactory: ConfirmSyncKeyDialogFactory, private val colorPickerFactory: ColorPickerDialogFactory, private val numberPickerFactory: NumberPickerFactory, private val behavior: Lazy @@ -172,7 +170,7 @@ class ListHabitsScreen } override fun showDeleteConfirmationScreen(callback: OnConfirmedCallback, quantity: Int) { - confirmDeleteDialogFactory.create(callback, quantity).show() + ConfirmDeleteDialog(activity, callback, quantity).show() } override fun showEditHabitsScreen(habits: List) { @@ -250,7 +248,7 @@ class ListHabitsScreen } override fun showConfirmInstallSyncKey(callback: OnConfirmedCallback) { - confirmSyncKeyDialogFactory.create(callback).show() + ConfirmSyncKeyDialog(activity, callback).show() } private fun getExecuteString(command: Command): String? { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt index ff70f8dd7..4192e8238 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt @@ -28,8 +28,6 @@ import android.text.TextPaint import android.view.HapticFeedbackConstants import android.view.View import android.view.View.MeasureSpec.EXACTLY -import com.google.auto.factory.AutoFactory -import com.google.auto.factory.Provided import org.isoron.uhabits.R import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.models.Entry.Companion.NO @@ -43,11 +41,19 @@ import org.isoron.uhabits.utils.getFontAwesome import org.isoron.uhabits.utils.showMessage import org.isoron.uhabits.utils.sres import org.isoron.uhabits.utils.toMeasureSpec +import javax.inject.Inject + +class CheckmarkButtonViewFactory +@Inject constructor( + @ActivityContext val context: Context, + val preferences: Preferences +) { + fun create() = CheckmarkButtonView(context, preferences) +} -@AutoFactory class CheckmarkButtonView( - @Provided @ActivityContext context: Context, - @Provided val preferences: Preferences + context: Context, + val preferences: Preferences ) : View(context), View.OnClickListener, View.OnLongClickListener { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt index 9aac2eaf6..0b1139e5c 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt @@ -20,19 +20,26 @@ package org.isoron.uhabits.activities.habits.list.views import android.content.Context -import com.google.auto.factory.AutoFactory -import com.google.auto.factory.Provided import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.utils.DateUtils import org.isoron.uhabits.inject.ActivityContext +import javax.inject.Inject + +class CheckmarkPanelViewFactory +@Inject constructor( + @ActivityContext val context: Context, + val preferences: Preferences, + private val buttonFactory: CheckmarkButtonViewFactory +) { + fun create() = CheckmarkPanelView(context, preferences, buttonFactory) +} -@AutoFactory class CheckmarkPanelView( - @Provided @ActivityContext context: Context, - @Provided preferences: Preferences, - @Provided private val buttonFactory: CheckmarkButtonViewFactory + context: Context, + preferences: Preferences, + private val buttonFactory: CheckmarkButtonViewFactory ) : ButtonPanelView(context, preferences) { var values = IntArray(0) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt index 5b2bcfe97..c1287b7b6 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt @@ -32,20 +32,28 @@ import androidx.recyclerview.widget.ItemTouchHelper.START import androidx.recyclerview.widget.ItemTouchHelper.UP import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.google.auto.factory.AutoFactory -import com.google.auto.factory.Provided import dagger.Lazy import org.isoron.uhabits.R import org.isoron.uhabits.activities.common.views.BundleSavedState import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.inject.ActivityContext +import javax.inject.Inject + +class HabitCardListViewFactory +@Inject constructor( + @ActivityContext val context: Context, + val adapter: HabitCardListAdapter, + val cardViewFactory: HabitCardViewFactory, + val controller: Lazy +) { + fun create() = HabitCardListView(context, adapter, cardViewFactory, controller) +} -@AutoFactory class HabitCardListView( - @Provided @ActivityContext context: Context, - @Provided private val adapter: HabitCardListAdapter, - @Provided private val cardViewFactory: HabitCardViewFactory, - @Provided private val controller: Lazy + @ActivityContext context: Context, + private val adapter: HabitCardListAdapter, + private val cardViewFactory: HabitCardViewFactory, + private val controller: Lazy ) : RecyclerView(context, null, R.attr.scrollableRecyclerViewStyle) { var checkmarkCount: Int = 0 diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt index 1f12d6eb7..6374cd419 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt @@ -34,8 +34,6 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout import android.widget.LinearLayout import android.widget.TextView -import com.google.auto.factory.AutoFactory -import com.google.auto.factory.Provided import org.isoron.uhabits.R import org.isoron.uhabits.activities.common.views.RingView import org.isoron.uhabits.core.models.Habit @@ -47,13 +45,23 @@ import org.isoron.uhabits.inject.ActivityContext import org.isoron.uhabits.utils.dp import org.isoron.uhabits.utils.sres import org.isoron.uhabits.utils.toThemedAndroidColor +import javax.inject.Inject + +class HabitCardViewFactory +@Inject constructor( + @ActivityContext val context: Context, + private val checkmarkPanelFactory: CheckmarkPanelViewFactory, + private val numberPanelFactory: NumberPanelViewFactory, + private val behavior: ListHabitsBehavior +) { + fun create() = HabitCardView(context, checkmarkPanelFactory, numberPanelFactory, behavior) +} -@AutoFactory class HabitCardView( - @Provided @ActivityContext context: Context, - @Provided private val checkmarkPanelFactory: CheckmarkPanelViewFactory, - @Provided private val numberPanelFactory: NumberPanelViewFactory, - @Provided private val behavior: ListHabitsBehavior + @ActivityContext context: Context, + private val checkmarkPanelFactory: CheckmarkPanelViewFactory, + private val numberPanelFactory: NumberPanelViewFactory, + private val behavior: ListHabitsBehavior ) : FrameLayout(context), ModelObservable.Listener { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt index 356b0bd46..c6bcfc21f 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonView.kt @@ -28,8 +28,6 @@ import android.text.TextPaint import android.view.View import android.view.View.OnClickListener import android.view.View.OnLongClickListener -import com.google.auto.factory.AutoFactory -import com.google.auto.factory.Provided import org.isoron.uhabits.R import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.inject.ActivityContext @@ -38,6 +36,7 @@ import org.isoron.uhabits.utils.StyledResources import org.isoron.uhabits.utils.getFontAwesome import org.isoron.uhabits.utils.showMessage import java.text.DecimalFormat +import javax.inject.Inject private val BOLD_TYPEFACE = Typeface.create("sans-serif-condensed", Typeface.BOLD) private val NORMAL_TYPEFACE = Typeface.create("sans-serif-condensed", Typeface.NORMAL) @@ -55,10 +54,17 @@ fun Double.toShortString(): String = when { else -> DecimalFormat("#.##").format(this) } -@AutoFactory +class NumberButtonViewFactory +@Inject constructor( + @ActivityContext val context: Context, + val preferences: Preferences +) { + fun create() = NumberButtonView(context, preferences) +} + class NumberButtonView( - @Provided @ActivityContext context: Context, - @Provided val preferences: Preferences + @ActivityContext context: Context, + val preferences: Preferences ) : View(context), OnClickListener, OnLongClickListener { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelView.kt index 25b17a8c2..8b4dcb028 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelView.kt @@ -20,18 +20,25 @@ package org.isoron.uhabits.activities.habits.list.views import android.content.Context -import com.google.auto.factory.AutoFactory -import com.google.auto.factory.Provided import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.utils.DateUtils import org.isoron.uhabits.inject.ActivityContext +import javax.inject.Inject + +class NumberPanelViewFactory +@Inject constructor( + @ActivityContext val context: Context, + val preferences: Preferences, + val buttonFactory: NumberButtonViewFactory +) { + fun create() = NumberPanelView(context, preferences, buttonFactory) +} -@AutoFactory class NumberPanelView( - @Provided @ActivityContext context: Context, - @Provided preferences: Preferences, - @Provided private val buttonFactory: NumberButtonViewFactory + @ActivityContext context: Context, + preferences: Preferences, + private val buttonFactory: NumberButtonViewFactory ) : ButtonPanelView(context, preferences) { var values = DoubleArray(0) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt index 9b22bdfef..10ddffd58 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt @@ -30,7 +30,6 @@ import org.isoron.uhabits.AndroidDirFinder import org.isoron.uhabits.HabitsApplication import org.isoron.uhabits.activities.AndroidThemeSwitcher import org.isoron.uhabits.activities.HabitsDirFinder -import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialogFactory import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory import org.isoron.uhabits.core.commands.Command import org.isoron.uhabits.core.commands.CommandRunner @@ -67,7 +66,6 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { val screen = ShowHabitScreen( activity = this, - confirmDeleteDialogFactory = ConfirmDeleteDialogFactory { this }, habit = habit, intentFactory = IntentFactory(), numberPickerFactory = NumberPickerFactory(this), diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.kt index 31d91fd42..5456c9bc4 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.kt @@ -20,7 +20,7 @@ package org.isoron.uhabits.activities.habits.show import org.isoron.uhabits.R -import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialogFactory +import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory import org.isoron.uhabits.core.models.Habit @@ -36,7 +36,6 @@ import org.isoron.uhabits.widgets.WidgetUpdater class ShowHabitScreen( val activity: ShowHabitActivity, - val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory, val habit: Habit, val intentFactory: IntentFactory, val numberPickerFactory: NumberPickerFactory, @@ -79,7 +78,7 @@ class ShowHabitScreen( } override fun showDeleteConfirmationScreen(callback: OnConfirmedCallback) { - confirmDeleteDialogFactory.create(callback, 1).show() + ConfirmDeleteDialog(activity, callback, 1).show() } override fun close() { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/inject/ActivityContextModule.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/inject/ActivityContextModule.java index 01ec64376..1efcf3c75 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/inject/ActivityContextModule.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/inject/ActivityContextModule.java @@ -19,9 +19,11 @@ package org.isoron.uhabits.inject; -import android.content.*; -import dagger.*; +import android.content.Context; + +import dagger.Module; +import dagger.Provides; @Module public class ActivityContextModule diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/inject/AppContextModule.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/inject/AppContextModule.java index 298c04c1d..394b638b8 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/inject/AppContextModule.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/inject/AppContextModule.java @@ -19,9 +19,10 @@ package org.isoron.uhabits.inject; -import android.content.*; +import android.content.Context; -import dagger.*; +import dagger.Module; +import dagger.Provides; @Module public class AppContextModule diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.java index 17df191c7..7dba32e07 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.java @@ -26,7 +26,8 @@ import org.isoron.uhabits.core.tasks.*; import java.util.*; -import dagger.*; +import dagger.Module; +import dagger.Provides; @Module public class AndroidTaskRunner implements TaskRunner diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java index fc00a62a2..9a553f7b7 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java @@ -21,10 +21,7 @@ package org.isoron.uhabits.tasks; import android.content.*; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.auto.factory.*; +import androidx.annotation.*; import org.isoron.uhabits.*; import org.isoron.uhabits.core.tasks.*; @@ -33,7 +30,6 @@ import org.isoron.uhabits.utils.*; import java.io.*; -@AutoFactory(allowSubclasses = true) public class ExportDBTask implements Task { private String filename; @@ -46,8 +42,8 @@ public class ExportDBTask implements Task @NonNull private final Listener listener; - public ExportDBTask(@Provided @AppContext @NonNull Context context, - @Provided @NonNull AndroidDirFinder system, + public ExportDBTask(@AppContext @NonNull Context context, + @NonNull AndroidDirFinder system, @NonNull Listener listener) { this.system = system; diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTaskFactory.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTaskFactory.kt new file mode 100644 index 000000000..af4998e13 --- /dev/null +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTaskFactory.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.tasks + +import android.content.Context +import org.isoron.uhabits.AndroidDirFinder +import org.isoron.uhabits.inject.AppContext +import javax.inject.Inject + +class ExportDBTaskFactory +@Inject constructor( + @AppContext private val context: Context, + private val system: AndroidDirFinder, +) { + fun create(listener: ExportDBTask.Listener) = ExportDBTask(context, system, listener) +} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java index b210ed70a..0e2d0ff9c 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java @@ -23,8 +23,6 @@ import android.util.*; import androidx.annotation.NonNull; -import com.google.auto.factory.*; - import org.isoron.uhabits.core.io.*; import org.isoron.uhabits.core.models.ModelFactory; import org.isoron.uhabits.core.models.sqlite.SQLModelFactory; @@ -32,7 +30,6 @@ import org.isoron.uhabits.core.tasks.*; import java.io.*; -@AutoFactory(allowSubclasses = true) public class ImportDataTask implements Task { public static final int FAILED = 3; @@ -53,8 +50,8 @@ public class ImportDataTask implements Task @NonNull private final Listener listener; - public ImportDataTask(@Provided @NonNull GenericImporter importer, - @Provided @NonNull ModelFactory modelFactory, + public ImportDataTask(@NonNull GenericImporter importer, + @NonNull ModelFactory modelFactory, @NonNull File file, @NonNull Listener listener) { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTaskFactory.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTaskFactory.kt new file mode 100644 index 000000000..7188c8cd7 --- /dev/null +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTaskFactory.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.tasks + +import org.isoron.uhabits.core.io.GenericImporter +import org.isoron.uhabits.core.models.ModelFactory +import java.io.File +import javax.inject.Inject + +class ImportDataTaskFactory +@Inject constructor( + private val importer: GenericImporter, + private val modelFactory: ModelFactory, +) { + fun create(file: File, listener: ImportDataTask.Listener) = + ImportDataTask(importer, modelFactory, file, listener) +} diff --git a/android/uhabits-core/build.gradle b/android/uhabits-core/build.gradle index 9f553108d..dacc583b7 100644 --- a/android/uhabits-core/build.gradle +++ b/android/uhabits-core/build.gradle @@ -3,9 +3,7 @@ apply plugin: 'java' apply plugin: 'kotlin' dependencies { - annotationProcessor "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION" annotationProcessor "com.google.dagger:dagger:$DAGGER_VERSION" - compileOnly "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION" compileOnly "com.google.dagger:dagger:$DAGGER_VERSION" compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'org.jetbrains:annotations:18.0.0' diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/tasks/ExportCSVTask.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/tasks/ExportCSVTask.java index 27e26b8fe..af3737fb3 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/tasks/ExportCSVTask.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/tasks/ExportCSVTask.java @@ -21,15 +21,12 @@ package org.isoron.uhabits.core.tasks; import androidx.annotation.*; -import com.google.auto.factory.*; - import org.isoron.uhabits.core.io.*; import org.isoron.uhabits.core.models.*; import java.io.*; import java.util.*; -@AutoFactory(allowSubclasses = true) public class ExportCSVTask implements Task { private String archiveFilename; @@ -45,7 +42,7 @@ public class ExportCSVTask implements Task @NonNull private final HabitList habitList; - public ExportCSVTask(@Provided @NonNull HabitList habitList, + public ExportCSVTask(@NonNull HabitList habitList, @NonNull List selectedHabits, @NonNull File outputDir, @NonNull Listener listener) diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/tasks/ExportCSVTaskFactory.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/tasks/ExportCSVTaskFactory.kt new file mode 100644 index 000000000..395ce0778 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/tasks/ExportCSVTaskFactory.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.tasks + +import org.isoron.uhabits.core.models.Habit +import org.isoron.uhabits.core.models.HabitList +import java.io.File +import javax.inject.Inject + +class ExportCSVTaskFactory +@Inject constructor( + val habitList: HabitList +) { + fun create( + selectedHabits: List, + outputDir: File, + listener: ExportCSVTask.Listener, + ) = ExportCSVTask(habitList, selectedHabits, outputDir, listener) +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HintList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HintList.java index 9c06dfa75..ff51614ea 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HintList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HintList.java @@ -21,8 +21,6 @@ package org.isoron.uhabits.core.ui.screens.habits.list; import androidx.annotation.*; -import com.google.auto.factory.*; - import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.preferences.*; import org.isoron.uhabits.core.utils.*; @@ -31,7 +29,6 @@ import org.isoron.uhabits.core.utils.*; * Provides a list of hints to be shown at the application startup, and takes * care of deciding when a new hint should be shown. */ -@AutoFactory public class HintList { private final Preferences prefs; @@ -44,7 +41,7 @@ public class HintList * * @param hints initial list of hints */ - public HintList(@Provided @NonNull Preferences prefs, + public HintList(@NonNull Preferences prefs, @NonNull String hints[]) { this.prefs = prefs; diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HintListFactory.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HintListFactory.kt new file mode 100644 index 000000000..8b48b6bd2 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HintListFactory.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.core.ui.screens.habits.list + +import org.isoron.uhabits.core.preferences.Preferences +import javax.inject.Inject + +class HintListFactory +@Inject constructor( + val preferences: Preferences, +) { + fun create(hints: Array) = HintList(preferences, hints) +}