From 7e98a6254172eb1902415cb93644a05540cacd11 Mon Sep 17 00:00:00 2001 From: KristianTashkov Date: Thu, 3 Sep 2020 22:50:09 +0300 Subject: [PATCH] Make reminder days into active days for the habit --- .../acceptance/steps/EditHabitSteps.java | 4 +- .../common/views/HistoryChartTest.java | 2 +- .../habits/show/views/NotesCardTest.java | 2 +- .../habits/show/views/SubtitleCardTest.java | 2 +- .../uhabits/widgets/CheckmarkWidgetTest.java | 2 +- .../activities/common/views/HistoryChart.java | 2 +- .../habits/edit/EditHabitActivity.kt | 35 +++++++++-------- .../habits/list/views/CheckmarkButtonView.kt | 5 ++- .../widgets/views/CheckmarkWidgetView.java | 6 ++- .../main/res/layout/activity_edit_habit.xml | 17 ++++---- .../src/main/res/values/strings.xml | 1 + .../java/org/isoron/uhabits/core/Config.java | 2 +- .../core/commands/EditHabitCommand.java | 5 ++- .../uhabits/core/io/RewireDBImporter.java | 12 +++--- .../isoron/uhabits/core/models/Checkmark.java | 9 ++++- .../uhabits/core/models/CheckmarkList.java | 15 ++++++- .../org/isoron/uhabits/core/models/Habit.java | 19 +++++++++ .../isoron/uhabits/core/models/Reminder.java | 14 +------ .../uhabits/core/models/Repetition.java | 7 ++-- .../isoron/uhabits/core/models/ScoreList.java | 18 +++++++-- .../uhabits/core/models/WeekdayList.java | 9 +++++ .../models/sqlite/records/HabitRecord.java | 12 +++--- .../uhabits/core/ui/NotificationTray.java | 4 +- .../src/main/resources/migrations/24.sql | 3 ++ .../org/isoron/uhabits/core/BaseUnitTest.java | 3 +- .../core/commands/CreateHabitCommandTest.java | 2 +- .../isoron/uhabits/core/io/ImportTest.java | 4 +- .../core/models/CheckmarkListTest.java | 39 +++++++++++++++++-- .../uhabits/core/models/HabitListTest.java | 2 +- .../isoron/uhabits/core/models/HabitTest.java | 15 +++---- .../models/sqlite/SQLiteHabitListTest.java | 2 +- .../sqlite/records/HabitRecordTest.java | 2 +- .../core/reminders/ReminderSchedulerTest.java | 12 +++--- 33 files changed, 191 insertions(+), 97 deletions(-) create mode 100644 android/uhabits-core/src/main/resources/migrations/24.sql diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java index af17ecd00..a19af5d74 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java @@ -69,9 +69,9 @@ public class EditHabitSteps onView(withId(R.id.done_button)).perform(click()); } - public static void clickReminderDays() + public static void clickActiveDays() { - onView(withId(R.id.reminderDatePicker)).perform(click()); + onView(withId(R.id.activeDaysDatePicker)).perform(click()); } public static void unselectAllDays() 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 1c88ed265..9a04291dd 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 @@ -80,7 +80,7 @@ public class HistoryChartTest extends BaseViewTest chart.setIsEditable(true); chart.tap(dpToPixels(340), dpToPixels(40)); - verify(controller).onToggleCheckmark(today, Checkmark.SKIP); + verify(controller).onToggleCheckmark(today, Checkmark.SKIP_MANUAL); chart.tap(dpToPixels(340), dpToPixels(40)); verify(controller).onToggleCheckmark(today, Checkmark.NO); chart.tap(dpToPixels(340), dpToPixels(40)); diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardTest.java index 6f91bf4d6..f33bc0bc2 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardTest.java @@ -50,7 +50,7 @@ public class NotesCardTest extends BaseViewTest super.setUp(); habit = fixtures.createLongHabit(); - habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + habit.setReminder(new Reminder(8, 30)); view = LayoutInflater .from(targetContext) diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardTest.java index 63aa74407..922130603 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardTest.java @@ -47,7 +47,7 @@ public class SubtitleCardTest extends BaseViewTest super.setUp(); habit = fixtures.createLongHabit(); - habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + habit.setReminder(new Reminder(8, 30)); view = LayoutInflater .from(targetContext) diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java index 7b5cd30c5..4dda7fb41 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java @@ -71,7 +71,7 @@ public class CheckmarkWidgetTest extends BaseViewTest // possible to capture intents sent to BroadcastReceivers. button.performClick(); sleep(1000); - assertThat(checkmarks.getTodayValue(), equalTo(SKIP)); + assertThat(checkmarks.getTodayValue(), equalTo(SKIP_MANUAL)); button.performClick(); sleep(1000); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java index 5644343ee..c299dcf81 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java @@ -400,7 +400,7 @@ public class HistoryChart extends ScrollableChart float round = dpToPixels(getContext(), 2); canvas.drawRoundRect(location, round, round, pSquareBg); - if (!isNumerical && checkmark == SKIP) + if (!isNumerical && (checkmark == SKIP_MANUAL || checkmark == SKIP_AUTO)) { pSquareBg.setColor(backgroundColor); pSquareBg.setStrokeWidth(columnWidth * 0.025f); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt index b42964e87..2390b9eef 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt @@ -54,7 +54,7 @@ class EditHabitActivity : AppCompatActivity() { var freqDen = 1 var reminderHour = -1 var reminderMin = -1 - var reminderDays: WeekdayList = WeekdayList.EVERY_DAY + var activeDays: WeekdayList = WeekdayList.EVERY_DAY override fun onCreate(state: Bundle?) { super.onCreate(state) @@ -77,8 +77,9 @@ class EditHabitActivity : AppCompatActivity() { if (habit.hasReminder()) { reminderHour = habit.reminder.hour reminderMin = habit.reminder.minute - reminderDays = habit.reminder.days } + activeDays = habit.activeDays + binding.nameInput.setText(habit.name) binding.questionInput.setText(habit.question) binding.notesInput.setText(habit.description) @@ -96,7 +97,7 @@ class EditHabitActivity : AppCompatActivity() { freqDen = state.getInt("freqDen") reminderHour = state.getInt("reminderHour") reminderMin = state.getInt("reminderMin") - reminderDays = WeekdayList(state.getInt("reminderDays")) + activeDays = WeekdayList(state.getInt("activeDays")) } updateColors() @@ -168,21 +169,21 @@ class EditHabitActivity : AppCompatActivity() { override fun onTimeCleared(view: RadialPickerLayout?) { reminderHour = -1 reminderMin = -1 - reminderDays = WeekdayList.EVERY_DAY populateReminder() } }, currentHour, currentMin, is24HourMode, androidColor) dialog.show(supportFragmentManager, "timePicker") } - binding.reminderDatePicker.setOnClickListener { + populateActiveDays() + binding.activeDaysDatePicker.setOnClickListener { val dialog = WeekdayPickerDialog() dialog.setListener { days -> - reminderDays = days - if (reminderDays.isEmpty) reminderDays = WeekdayList.EVERY_DAY - populateReminder() + activeDays = days + if (activeDays.isEmpty) activeDays = WeekdayList.EVERY_DAY + populateActiveDays() } - dialog.setSelectedDays(reminderDays) + dialog.setSelectedDays(activeDays) dialog.show(supportFragmentManager, "dayPicker") } @@ -210,8 +211,9 @@ class EditHabitActivity : AppCompatActivity() { habit.description = notesInput.text.trim().toString() habit.color = paletteColor if (reminderHour >= 0) { - habit.setReminder(Reminder(reminderHour, reminderMin, reminderDays)) + habit.setReminder(Reminder(reminderHour, reminderMin)) } + habit.activeDays = activeDays habit.frequency = Frequency(freqNum, freqDen) if (habitType == Habit.NUMBER_HABIT) { habit.targetValue = targetInput.text.toString().toDouble() @@ -251,18 +253,17 @@ class EditHabitActivity : AppCompatActivity() { private fun populateReminder() { if (reminderHour < 0) { binding.reminderTimePicker.text = getString(R.string.reminder_off) - binding.reminderDatePicker.visibility = View.GONE - binding.reminderDivider.visibility = View.GONE } else { val time = AndroidDateUtils.formatTime(this, reminderHour, reminderMin) - val daysArray = reminderDays.toArray() binding.reminderTimePicker.text = time - binding.reminderDatePicker.visibility = View.VISIBLE - binding.reminderDivider.visibility = View.VISIBLE - binding.reminderDatePicker.text = AndroidDateUtils.formatWeekdayList(this, daysArray) } } + private fun populateActiveDays() { + val daysArray = activeDays.toArray() + binding.activeDaysDatePicker.text = AndroidDateUtils.formatWeekdayList(this, daysArray) + } + private fun populateFrequency() { binding.booleanFrequencyPicker.text = when { freqNum == 1 && freqDen == 1 -> getString(R.string.every_day) @@ -301,7 +302,7 @@ class EditHabitActivity : AppCompatActivity() { putInt("freqDen", freqDen) putInt("reminderHour", reminderHour) putInt("reminderMin", reminderMin) - putInt("reminderDays", reminderDays.toInteger()) + putInt("activeDays", activeDays.toInteger()) } } } 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 eded35446..4ce9bf5ed 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 @@ -104,11 +104,12 @@ class CheckmarkButtonView( fun draw(canvas: Canvas) { paint.color = when (value) { YES_MANUAL -> color - SKIP -> color + SKIP_MANUAL -> color else -> lowContrastColor } val id = when (value) { - SKIP -> R.string.fa_skipped + SKIP_MANUAL -> R.string.fa_skipped + SKIP_AUTO -> R.string.fa_skipped NO -> R.string.fa_times else -> R.string.fa_check } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java index 8b4e08bc8..daacf2d6c 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java @@ -76,7 +76,7 @@ public class CheckmarkWidgetView extends HabitWidgetView { switch (checkmarkState) { case Checkmark.YES_MANUAL: - case Checkmark.SKIP: + case Checkmark.SKIP_MANUAL: bgColor = activeColor; fgColor = res.getColor(R.attr.highContrastReverseTextColor); setShadowAlpha(0x4f); @@ -85,6 +85,7 @@ public class CheckmarkWidgetView extends HabitWidgetView { break; case Checkmark.YES_AUTO: + case Checkmark.SKIP_AUTO: case Checkmark.NO: default: getResources().getString(R.string.fa_times); @@ -118,7 +119,8 @@ public class CheckmarkWidgetView extends HabitWidgetView { case Checkmark.YES_MANUAL: case Checkmark.YES_AUTO: return getResources().getString(R.string.fa_check); - case Checkmark.SKIP: + case Checkmark.SKIP_MANUAL: + case Checkmark.SKIP_AUTO: return getResources().getString(R.string.fa_skipped); case Checkmark.NO: default: diff --git a/android/uhabits-android/src/main/res/layout/activity_edit_habit.xml b/android/uhabits-android/src/main/res/layout/activity_edit_habit.xml index 0701734e7..f720d86d9 100644 --- a/android/uhabits-android/src/main/res/layout/activity_edit_habit.xml +++ b/android/uhabits-android/src/main/res/layout/activity_edit_habit.xml @@ -199,19 +199,22 @@ style="@style/FormDropdown" android:id="@+id/reminderTimePicker" android:text="@string/reminder_off" /> + + - - + + + + - + - diff --git a/android/uhabits-android/src/main/res/values/strings.xml b/android/uhabits-android/src/main/res/values/strings.xml index 1ade96853..4c27bcdf5 100644 --- a/android/uhabits-android/src/main/res/values/strings.xml +++ b/android/uhabits-android/src/main/res/values/strings.xml @@ -179,6 +179,7 @@ First day of the week Have you completed this habit today? Notes + Active on (Optional) e.g. Did you wake up early today? Did you exercise? Did you play chess? Measurable diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/Config.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/Config.java index 286755a33..1a5383cee 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/Config.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/Config.java @@ -22,5 +22,5 @@ package org.isoron.uhabits.core; public class Config { public static final String DATABASE_FILENAME = "uhabits.db"; - public static int DATABASE_VERSION = 23; + public static int DATABASE_VERSION = 24; } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/commands/EditHabitCommand.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/commands/EditHabitCommand.java index a942b1f5d..20ea5b39a 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/commands/EditHabitCommand.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/commands/EditHabitCommand.java @@ -46,6 +46,8 @@ public class EditHabitCommand extends Command final boolean hasTargetChanged; + final boolean hasActiveDaysChanged; + public EditHabitCommand(@Provided @NonNull ModelFactory modelFactory, @NonNull HabitList habitList, @NonNull Habit original, @@ -68,6 +70,7 @@ public class EditHabitCommand extends Command hasTargetChanged = (original.getTargetType() != modified.getTargetType() || original.getTargetValue() != modified.getTargetValue()); + hasActiveDaysChanged = original.getActiveDays() != modified.getActiveDays(); } @Override @@ -97,7 +100,7 @@ public class EditHabitCommand extends Command habit.copyFrom(model); habitList.update(habit); - if (hasFrequencyChanged || hasTargetChanged) + if (hasFrequencyChanged || hasTargetChanged || hasActiveDaysChanged) habit.invalidateNewerThan(Timestamp.ZERO); } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/io/RewireDBImporter.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/io/RewireDBImporter.java index ac0d83c79..6e1b144ac 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/io/RewireDBImporter.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/io/RewireDBImporter.java @@ -189,21 +189,21 @@ public class RewireDBImporter extends AbstractImporter int rewireReminder = Integer.parseInt(c.getString(0)); if (rewireReminder <= 0 || rewireReminder >= 1440) return; - boolean reminderDays[] = new boolean[7]; + boolean activeDays[] = new boolean[7]; - String activeDays[] = c.getString(1).split(","); - for (String d : activeDays) + String rewireActiveDays[] = c.getString(1).split(","); + for (String d : rewireActiveDays) { int idx = (Integer.parseInt(d) + 1) % 7; - reminderDays[idx] = true; + activeDays[idx] = true; } int hour = rewireReminder / 60; int minute = rewireReminder % 60; - WeekdayList days = new WeekdayList(reminderDays); - Reminder reminder = new Reminder(hour, minute, days); + Reminder reminder = new Reminder(hour, minute); habit.setReminder(reminder); + habit.setActiveDays(new WeekdayList(activeDays)); habitList.update(habit); } finally diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Checkmark.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Checkmark.java index a3150e150..540cd4976 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Checkmark.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Checkmark.java @@ -37,10 +37,15 @@ import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle; @ThreadSafe public final class Checkmark { + /** + * Indicates that there was an implicit skip at the timestamp. + */ + public static final int SKIP_AUTO = 4; + /** * Indicates that there was an explicit skip at the timestamp. */ - public static final int SKIP = 3; + public static final int SKIP_MANUAL = 3; /** * Indicates that there was a repetition at the timestamp. @@ -64,7 +69,7 @@ public final class Checkmark /** * The value of the checkmark. *

- * For boolean habits, this equals either NO, YES_AUTO, YES_MANUAL or SKIP. + * For boolean habits, this equals either NO, YES_AUTO, YES_MANUAL, SKIP_MANUAL or SKIP_AUTO. *

* For numerical habits, this number is stored in thousandths. That * is, if the user enters value 1.50 on the app, it is stored as 1500. diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java index 5b3f3aed7..8935e745d 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java @@ -51,7 +51,8 @@ public abstract class CheckmarkList @NonNull static List buildCheckmarksFromIntervals(Repetition[] reps, - ArrayList intervals) + ArrayList intervals, + boolean[] activeDays) { if (reps.length == 0) throw new IllegalArgumentException(); @@ -75,6 +76,16 @@ public abstract class CheckmarkList } } + int realWeekday = + DateUtils.getStartOfTodayCalendar().get(Calendar.DAY_OF_WEEK) % 7; + for (int i = 0; i < nDays; i++) + { + if (!activeDays[realWeekday]) + checkmarks.set(i, new Checkmark(today.minus(i), SKIP_AUTO)); + realWeekday = (realWeekday + 6) % 7; + } + + for (Repetition rep : reps) { Timestamp date = rep.getTimestamp(); @@ -386,7 +397,7 @@ public abstract class CheckmarkList ArrayList intervals; intervals = buildIntervals(habit.getFrequency(), reps); snapIntervalsTogether(intervals); - add(buildCheckmarksFromIntervals(reps, intervals)); + add(buildCheckmarksFromIntervals(reps, intervals, habit.getActiveDays().toArray())); } public List getAll() { diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java index 5b2700514..1d9b7dfe5 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java @@ -279,6 +279,17 @@ public class Habit data.unit = unit; } + @NonNull + public synchronized WeekdayList getActiveDays() + { + return data.activeDays; + } + + public synchronized void setActiveDays(@NonNull WeekdayList activeDays) + { + data.activeDays = activeDays; + } + /** * Returns the public URI that identifies this habit * @@ -393,6 +404,9 @@ public class Habit @Nullable public Reminder reminder; + @Nullable + private WeekdayList activeDays; + public int position; public HabitData() @@ -408,6 +422,7 @@ public class Habit this.targetValue = 100; this.unit = ""; this.position = 0; + this.activeDays = WeekdayList.EVERY_DAY; } public HabitData(@NonNull HabitData model) @@ -424,6 +439,7 @@ public class Habit this.unit = model.unit; this.reminder = model.reminder; this.position = model.position; + this.activeDays = model.activeDays; } @Override @@ -442,6 +458,7 @@ public class Habit .append("reminder", reminder) .append("position", position) .append("question", question) + .append("activeDays", activeDays) .toString(); } @@ -467,6 +484,7 @@ public class Habit .append(reminder, habitData.reminder) .append(position, habitData.position) .append(question, habitData.question) + .append(activeDays, habitData.activeDays) .isEquals(); } @@ -486,6 +504,7 @@ public class Habit .append(reminder) .append(position) .append(question) + .append(activeDays) .toHashCode(); } } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Reminder.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Reminder.java index 6b2cf8278..df3e66b7b 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Reminder.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Reminder.java @@ -32,19 +32,10 @@ public final class Reminder private final int minute; - private final WeekdayList days; - - public Reminder(int hour, int minute, @NonNull WeekdayList days) + public Reminder(int hour, int minute) { this.hour = hour; this.minute = minute; - this.days = days; - } - - @NonNull - public WeekdayList getDays() - { - return days; } public int getHour() @@ -74,7 +65,6 @@ public final class Reminder return new EqualsBuilder() .append(hour, reminder.hour) .append(minute, reminder.minute) - .append(days, reminder.days) .isEquals(); } @@ -84,7 +74,6 @@ public final class Reminder return new HashCodeBuilder(17, 37) .append(hour) .append(minute) - .append(days) .toHashCode(); } @@ -94,7 +83,6 @@ public final class Reminder return new ToStringBuilder(this, defaultToStringStyle()) .append("hour", hour) .append("minute", minute) - .append("days", days) .toString(); } } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Repetition.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Repetition.java index 8b5fa2ff9..c62a98cc6 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Repetition.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Repetition.java @@ -36,7 +36,7 @@ public final class Repetition /** * The value of the repetition. * - * For boolean habits, this equals YES_MANUAL if performed or SKIP if skipped. + * For boolean habits, this equals YES_MANUAL if performed or SKIP_MANUAL if skipped. * For numerical habits, this number is stored in thousandths. That is, if the user enters * value 1.50 on the app, it is here stored as 1500. */ @@ -61,11 +61,12 @@ public final class Repetition switch(value) { case NO: case YES_AUTO: + case SKIP_AUTO: return YES_MANUAL; case YES_MANUAL: - return SKIP; + return SKIP_MANUAL; default: - case SKIP: + case SKIP_MANUAL: return NO; } } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java index 748e8f2cc..8b1119f3a 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java @@ -267,21 +267,33 @@ public abstract class ScoreList implements Iterable { if (from.isNewerThan(to)) return; - final double freq = habit.getFrequency().toDouble(); + final int activeDays = habit.getActiveDays().count(); + final Frequency frequency = habit.getFrequency(); + final double nonSkippedDays = (double)frequency.getDenominator() * activeDays / 7; + final double freq = Math.min((double)frequency.getNumerator() / nonSkippedDays, 1); final int checkmarkValues[] = habit.getCheckmarks().getValues(from, to); List scores = new LinkedList<>(); for (int i = 0; i < checkmarkValues.length; i++) { + boolean isSkip = false; double value = checkmarkValues[checkmarkValues.length - i - 1]; if (habit.isNumerical()) { value /= 1000; value /= habit.getTargetValue(); + value = Math.min(1, value); + } else if(value == Checkmark.SKIP_AUTO) + { + isSkip = true; + } + else value = Math.min(1, value); + + if(!isSkip) + { + previousValue = Score.compute(freq, previousValue, value); } - value = Math.min(1, value); - previousValue = Score.compute(freq, previousValue, value); scores.add(new Score(from.plus(i), previousValue)); } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/WeekdayList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/WeekdayList.java index 67325e915..39680b42a 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/WeekdayList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/WeekdayList.java @@ -73,6 +73,15 @@ public final class WeekdayList return packedList; } + public int count() { + int count = 0; + for (int i = 0; i < 7; i++) + { + if (weekdays[i]) count += 1; + } + return count; + } + @Override public boolean equals(Object o) { diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecord.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecord.java index 523da0542..610c1e022 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecord.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecord.java @@ -57,8 +57,8 @@ public class HabitRecord @Column(name = "reminder_min") public Integer reminderMin; - @Column(name = "reminder_days") - public Integer reminderDays; + @Column(name = "active_days") + public Integer activeDays; @Column public Integer highlight; @@ -99,7 +99,7 @@ public class HabitRecord Frequency freq = model.getFrequency(); this.freqNum = freq.getNumerator(); this.freqDen = freq.getDenominator(); - this.reminderDays = 0; + this.activeDays = 0; this.reminderMin = null; this.reminderHour = null; @@ -108,8 +108,8 @@ public class HabitRecord Reminder reminder = model.getReminder(); this.reminderHour = reminder.getHour(); this.reminderMin = reminder.getMinute(); - this.reminderDays = reminder.getDays().toInteger(); } + this.activeDays = model.getActiveDays().toInteger(); } public void copyTo(Habit habit) @@ -129,8 +129,8 @@ public class HabitRecord if (reminderHour != null && reminderMin != null) { - habit.setReminder(new Reminder(reminderHour, reminderMin, - new WeekdayList(reminderDays))); + habit.setReminder(new Reminder(reminderHour, reminderMin)); } + habit.setActiveDays(new WeekdayList(this.activeDays)); } } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/NotificationTray.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/NotificationTray.java index 2c348470b..aaa8b35d1 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/NotificationTray.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/NotificationTray.java @@ -219,10 +219,10 @@ public class NotificationTray if (!habit.hasReminder()) return false; Reminder reminder = habit.getReminder(); - boolean reminderDays[] = reminder.getDays().toArray(); + boolean activeDays[] = habit.getActiveDays().toArray(); int weekday = timestamp.getWeekday(); - return reminderDays[weekday]; + return activeDays[weekday]; } } } diff --git a/android/uhabits-core/src/main/resources/migrations/24.sql b/android/uhabits-core/src/main/resources/migrations/24.sql new file mode 100644 index 000000000..7cbd71fef --- /dev/null +++ b/android/uhabits-core/src/main/resources/migrations/24.sql @@ -0,0 +1,3 @@ +alter table Habits add column active_days integer not null default 127; + +update Habits set active_days = reminder_days diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/BaseUnitTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/BaseUnitTest.java index d16902678..3d4ff8505 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/BaseUnitTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/BaseUnitTest.java @@ -38,6 +38,7 @@ import java.io.*; import java.sql.*; import java.util.*; +import static org.isoron.uhabits.core.Config.*; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) @@ -125,7 +126,7 @@ public class BaseUnitTest DriverManager.getConnection("jdbc:sqlite::memory:")); db.execute("pragma user_version=8;"); MigrationHelper helper = new MigrationHelper(db); - helper.migrateTo(23); + helper.migrateTo(DATABASE_VERSION); return db; } catch (SQLException e) diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/commands/CreateHabitCommandTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/commands/CreateHabitCommandTest.java index 5d37b1bfa..e5afaede6 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/commands/CreateHabitCommandTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/commands/CreateHabitCommandTest.java @@ -41,7 +41,7 @@ public class CreateHabitCommandTest extends BaseUnitTest model = fixtures.createEmptyHabit(); model.setName("New habit"); - model.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + model.setReminder(new Reminder(8, 30)); command = new CreateHabitCommand(modelFactory, habitList, model); } diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/io/ImportTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/io/ImportTest.java index 73671d7e7..74664b012 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/io/ImportTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/io/ImportTest.java @@ -98,8 +98,8 @@ public class ImportTest extends BaseUnitTest Reminder reminder = habit.getReminder(); assertThat(reminder.getHour(), equalTo(8)); assertThat(reminder.getMinute(), equalTo(0)); - boolean[] reminderDays = { false, true, true, true, true, true, false }; - assertThat(reminder.getDays().toArray(), equalTo(reminderDays)); + boolean[] activeDays = { false, true, true, true, true, true, false }; + assertThat(habit.getActiveDays().toArray(), equalTo(activeDays)); } @Test diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/CheckmarkListTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/CheckmarkListTest.java index 802ca9e23..5500e8278 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/CheckmarkListTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/CheckmarkListTest.java @@ -93,7 +93,8 @@ public class CheckmarkListTest extends BaseUnitTest expected.add(new Checkmark(day(10), YES_MANUAL)); List actual = - CheckmarkList.buildCheckmarksFromIntervals(reps, intervals); + CheckmarkList.buildCheckmarksFromIntervals( + reps, intervals, WeekdayList.EVERY_DAY.toArray()); assertThat(actual, equalTo(expected)); } @@ -111,7 +112,39 @@ public class CheckmarkListTest extends BaseUnitTest expected.add(new Checkmark(day(0), YES_MANUAL)); List actual = - CheckmarkList.buildCheckmarksFromIntervals(reps, intervals); + CheckmarkList.buildCheckmarksFromIntervals( + reps, intervals, WeekdayList.EVERY_DAY.toArray()); + assertThat(actual, equalTo(expected)); + } + + @Test + public void test_buildCheckmarksFromIntervals_3() throws Exception + { + Repetition reps[] = new Repetition[]{ + new Repetition(day(10), YES_MANUAL), + new Repetition(day(1), YES_MANUAL), + }; + + ArrayList intervals = new ArrayList<>(); + intervals.add(new CheckmarkList.Interval(day(10), day(10), day(9))); + intervals.add(new CheckmarkList.Interval(day(1), day(1), day(0))); + + List expected = new ArrayList<>(); + expected.add(new Checkmark(day(0), SKIP_AUTO)); + expected.add(new Checkmark(day(1), YES_MANUAL)); + expected.add(new Checkmark(day(2), NO)); + expected.add(new Checkmark(day(3), NO)); + expected.add(new Checkmark(day(4), NO)); + expected.add(new Checkmark(day(5), NO)); + expected.add(new Checkmark(day(6), NO)); + expected.add(new Checkmark(day(7), SKIP_AUTO)); + expected.add(new Checkmark(day(8), SKIP_AUTO)); + expected.add(new Checkmark(day(9), YES_AUTO)); + expected.add(new Checkmark(day(10), YES_MANUAL)); + + boolean[] activeDays = {false, false, true, true, true, true, true}; + List actual = + CheckmarkList.buildCheckmarksFromIntervals(reps, intervals, activeDays); assertThat(actual, equalTo(expected)); } @@ -181,7 +214,7 @@ public class CheckmarkListTest extends BaseUnitTest { Repetition[] reps = new Repetition[]{ new Repetition(day(30), YES_MANUAL), - new Repetition(day(20), SKIP), + new Repetition(day(20), SKIP_MANUAL), new Repetition(day(10), YES_MANUAL), }; diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/HabitListTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/HabitListTest.java index d5fdc448a..f451c3dca 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/HabitListTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/HabitListTest.java @@ -58,7 +58,7 @@ public class HabitListTest extends BaseUnitTest habitsArray.add(habit); if (i % 3 == 0) - habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + habit.setReminder(new Reminder(8, 30)); } habitsArray.get(0).setArchived(true); diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/HabitTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/HabitTest.java index e99e954c0..9723296aa 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/HabitTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/HabitTest.java @@ -60,7 +60,8 @@ public class HabitTest extends BaseUnitTest model.setArchived(true); model.setColor(0); model.setFrequency(new Frequency(10, 20)); - model.setReminder(new Reminder(8, 30, new WeekdayList(1))); + model.setReminder(new Reminder(8, 30)); + model.setActiveDays(new WeekdayList(1)); Habit habit = modelFactory.buildHabit(); habit.copyFrom(model); @@ -77,7 +78,7 @@ public class HabitTest extends BaseUnitTest Habit h = modelFactory.buildHabit(); assertThat(h.hasReminder(), is(false)); - h.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + h.setReminder(new Reminder(8, 30)); assertThat(h.hasReminder(), is(true)); h.clearReminder(); @@ -148,15 +149,15 @@ public class HabitTest extends BaseUnitTest public void testToString() throws Exception { Habit h = modelFactory.buildHabit(); - h.setReminder(new Reminder(22, 30, WeekdayList.EVERY_DAY)); + h.setReminder(new Reminder(22, 30)); String expected = "{id: , data: {name: , description: ," + " frequency: {numerator: 3, denominator: 7}," + " color: 8, archived: false, targetType: 0," + " targetValue: 100.0, type: 0, unit: ," + - " reminder: {hour: 22, minute: 30," + - " days: {weekdays: [true,true,true,true,true,true,true]}}," + - " position: 0, question: }}"; + " reminder: {hour: 22, minute: 30}," + + " position: 0, question: ," + + " activeDays: {weekdays: [true,true,true,true,true,true,true]}}}"; - assertThat(h.toString(), equalTo(expected)); + assertThat(h.toString(), equalTo(expected)); } } \ No newline at end of file diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.java index a78313417..f5e047940 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.java @@ -70,7 +70,7 @@ public class SQLiteHabitListTest extends BaseUnitTest habitsArray.add(habit); if (i % 3 == 0) - habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + habit.setReminder(new Reminder(8, 30)); } habitsArray.get(0).setArchived(true); diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecordTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecordTest.java index e6b016a66..658ea0fdf 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecordTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecordTest.java @@ -40,7 +40,7 @@ public class HabitRecordTest extends BaseUnitTest original.setColor(1); original.setArchived(true); original.setFrequency(Frequency.THREE_TIMES_PER_WEEK); - original.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + original.setReminder(new Reminder(8, 30)); original.setId(1000L); original.setPosition(20); diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.java index 4f8790c73..66b752874 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.java @@ -73,8 +73,8 @@ public class ReminderSchedulerTest extends BaseUnitTest Habit h1 = fixtures.createEmptyHabit(); Habit h2 = fixtures.createEmptyHabit(); Habit h3 = fixtures.createEmptyHabit(); - h1.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); - h2.setReminder(new Reminder(18, 30, WeekdayList.EVERY_DAY)); + h1.setReminder(new Reminder(8, 30)); + h2.setReminder(new Reminder(18, 30)); h3.setReminder(null); habitList.add(h1); habitList.add(h2); @@ -94,7 +94,7 @@ public class ReminderSchedulerTest extends BaseUnitTest long atTime = unixTime(2015, 1, 30, 11, 30); long expectedCheckmarkTime = unixTime(2015, 1, 30, 0, 0); - habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + habit.setReminder(new Reminder(8, 30)); scheduleAndVerify(atTime, expectedCheckmarkTime, atTime); } @@ -109,7 +109,7 @@ public class ReminderSchedulerTest extends BaseUnitTest long regularReminderTime = applyTimezone(unixTime(2015, 1, 2, 8, 30)); long todayCheckmarkTime = unixTime(2015, 1, 1, 0, 0); long tomorrowCheckmarkTime = unixTime(2015, 1, 2, 0, 0); - habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + habit.setReminder(new Reminder(8, 30)); when(widgetPreferences.getSnoozeTime(habitId)).thenReturn(snoozeTimeInFuture); reminderScheduler.schedule(habit); @@ -129,7 +129,7 @@ public class ReminderSchedulerTest extends BaseUnitTest long expectedCheckmarkTime = unixTime(2015, 1, 26, 0, 0); long expectedReminderTime = unixTime(2015, 1, 26, 12, 30); - habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + habit.setReminder(new Reminder(8, 30)); scheduleAndVerify(null, expectedCheckmarkTime, expectedReminderTime); } @@ -142,7 +142,7 @@ public class ReminderSchedulerTest extends BaseUnitTest long expectedCheckmarkTime = unixTime(2015, 1, 27, 0, 0); long expectedReminderTime = unixTime(2015, 1, 27, 12, 30); - habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY)); + habit.setReminder(new Reminder(8, 30)); scheduleAndVerify(null, expectedCheckmarkTime, expectedReminderTime); }