checkmarks = new ArrayList<>(nDays);
for (int i = 0; i < nDays; i++)
- checkmarks.add(new Checkmark(today.minus(i), NO));
+ checkmarks.add(new Checkmark(today.minus(i), UNKNOWN));
for (Interval interval : intervals)
{
@@ -79,7 +79,10 @@ public abstract class CheckmarkList
{
Timestamp date = rep.getTimestamp();
int offset = date.daysUntil(today);
- checkmarks.set(offset, new Checkmark(date, rep.getValue()));
+ int value = rep.getValue();
+ int prevValue = checkmarks.get(offset).getValue();
+ if (prevValue < value)
+ checkmarks.set(offset, new Checkmark(date, value));
}
return checkmarks;
@@ -224,7 +227,7 @@ public abstract class CheckmarkList
{
Checkmark today = getToday();
if (today != null) return today.getValue();
- else return NO;
+ else return UNKNOWN;
}
public synchronized int getThisWeekValue(int firstWeekday)
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 2eef8245e..2c3ff86d4 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
@@ -332,7 +332,7 @@ public class Habit
else
return todayCheckmark / 1000.0 <= data.targetValue;
}
- else return (todayCheckmark != NO);
+ else return (todayCheckmark != NO && todayCheckmark != UNKNOWN);
}
public synchronized boolean isNumerical()
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 a41546cb8..9c3ec30f9 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
@@ -60,6 +60,7 @@ public final class Repetition
{
switch(value) {
case NO:
+ case UNKNOWN:
case YES_AUTO:
return YES_MANUAL;
case YES_MANUAL:
@@ -74,6 +75,7 @@ public final class Repetition
{
switch(value) {
case NO:
+ case UNKNOWN:
case YES_AUTO:
return YES_MANUAL;
default:
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/RepetitionList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/RepetitionList.java
index f5c9a268c..682c865e3 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/RepetitionList.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/RepetitionList.java
@@ -52,19 +52,6 @@ public abstract class RepetitionList
*/
public abstract void add(Repetition repetition);
- /**
- * Returns true if the list contains a repetition that has the given
- * timestamp.
- *
- * @param timestamp the timestamp to find.
- * @return true if list contains repetition with given timestamp, false
- * otherwise.
- */
- public boolean containsTimestamp(Timestamp timestamp)
- {
- return (getByTimestamp(timestamp) != null);
- }
-
/**
* Returns the list of repetitions that happened within the given time
* interval.
@@ -90,6 +77,18 @@ public abstract class RepetitionList
@Nullable
public abstract Repetition getByTimestamp(Timestamp timestamp);
+ /**
+ * If a repetition with the given timestamp exists, return its value. Otherwise, returns
+ * Checkmark.NO for boolean habits and zero for numerical habits.
+ */
+ @NonNull
+ public int getValue(Timestamp timestamp)
+ {
+ Repetition rep = getByTimestamp(timestamp);
+ if (rep == null) return Checkmark.UNKNOWN;
+ return rep.getValue();
+ }
+
@NonNull
public ModelObservable getObservable()
{
@@ -175,39 +174,9 @@ public abstract class RepetitionList
*/
public abstract void remove(@NonNull Repetition repetition);
- /**
- * Adds or remove a repetition at a certain timestamp.
- *
- * If there exists a repetition on the list with the given timestamp, the
- * method removes this repetition from the list and returns it. If there are
- * no repetitions with the given timestamp, creates and adds one to the
- * list, then returns it.
- *
- * @param timestamp the timestamp for the timestamp that should be added or
- * removed.
- * @return the repetition that has been added or removed.
- */
- @NonNull
- public synchronized Repetition toggle(Timestamp timestamp)
- {
- if (habit.isNumerical())
- throw new IllegalStateException("habit must NOT be numerical");
-
- Repetition rep = getByTimestamp(timestamp);
- if (rep != null) remove(rep);
- else
- {
- rep = new Repetition(timestamp, Checkmark.YES_MANUAL);
- add(rep);
- }
-
- habit.invalidateNewerThan(timestamp);
- return rep;
- }
-
public abstract long getTotalCount();
- public void toggle(Timestamp timestamp, int value)
+ public void setValue(Timestamp timestamp, int value)
{
Repetition rep = getByTimestamp(timestamp);
if (rep != null) remove(rep);
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java
index 036ca2119..054c197cc 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java
@@ -138,8 +138,8 @@ public abstract class StreakList
current = current.plus(1);
int j = checks.length - i - 1;
- if ((checks[j + 1] == 0 && checks[j] > 0)) list.add(current);
- if ((checks[j + 1] > 0 && checks[j] == 0)) list.add(current.minus(1));
+ if ((checks[j + 1] <= 0 && checks[j] > 0)) list.add(current);
+ if ((checks[j + 1] > 0 && checks[j] <= 0)) list.add(current.minus(1));
}
if (list.size() % 2 == 1) list.add(current);
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/memory/MemoryCheckmarkList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/memory/MemoryCheckmarkList.java
index 3504c2a3d..b6f8e29b9 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/memory/MemoryCheckmarkList.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/memory/MemoryCheckmarkList.java
@@ -68,7 +68,7 @@ public class MemoryCheckmarkList extends CheckmarkList
{
Timestamp t = to.minus(i);
if(t.isNewerThan(newestComputed) || t.isOlderThan(oldestComputed))
- filtered.add(new Checkmark(t, Checkmark.NO));
+ filtered.add(new Checkmark(t, Checkmark.UNKNOWN));
else
filtered.add(list.get(t.daysUntil(newestComputed)));
}
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
index 23ce47d5c..dd36ae983 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
@@ -345,6 +345,10 @@ public class Preferences
if(enabled) for (Listener l : listeners) l.onSyncEnabled();
}
+ public boolean areQuestionMarksEnabled()
+ {
+ return storage.getBoolean("pref_unknown_enabled", false);
+ }
/**
* @return An integer representing the first day of the week. Sunday
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/test/HabitFixtures.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/test/HabitFixtures.java
index 5e1e3edb5..4ab2675a8 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/test/HabitFixtures.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/test/HabitFixtures.java
@@ -23,6 +23,8 @@ import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.*;
import org.isoron.uhabits.core.utils.*;
+import static org.isoron.uhabits.core.models.Checkmark.*;
+
public class HabitFixtures
{
public boolean NON_DAILY_HABIT_CHECKS[] = {
@@ -63,7 +65,7 @@ public class HabitFixtures
81, 83, 89, 90, 91, 95, 102, 103, 108, 109, 120};
for (int mark : marks)
- habit.getRepetitions().toggle(today.minus(mark));
+ habit.getRepetitions().setValue(today.minus(mark), YES_MANUAL);
return habit;
}
@@ -87,7 +89,7 @@ public class HabitFixtures
for (int i = 0; i < times.length; i++)
{
Timestamp timestamp = today.minus(times[i]);
- habit.getRepetitions().add(new Repetition(timestamp, values[i]));
+ habit.getRepetitions().setValue(timestamp, values[i]);
}
return habit;
@@ -123,7 +125,7 @@ public class HabitFixtures
for (int i = 0; i < times.length; i++)
{
Timestamp timestamp = reference.minus(times[i]);
- habit.getRepetitions().add(new Repetition(timestamp, values[i]));
+ habit.getRepetitions().setValue(timestamp, values[i]);
}
return habit;
@@ -140,7 +142,9 @@ public class HabitFixtures
Timestamp timestamp = DateUtils.getToday();
for (boolean c : NON_DAILY_HABIT_CHECKS)
{
- if (c) habit.getRepetitions().toggle(timestamp);
+ int value = NO;
+ if (c) value = YES_MANUAL;
+ habit.getRepetitions().setValue(timestamp, value);
timestamp = timestamp.minus(1);
}
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 4adb888a8..266b84a74 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
@@ -186,7 +186,7 @@ public class NotificationTray
{
systemTray.log("Showing notification for habit=" + habit.id);
- if (todayValue != Checkmark.NO) {
+ if (todayValue != Checkmark.UNKNOWN) {
systemTray.log(String.format(
Locale.US,
"Habit %d already checked. Skipping.",
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehavior.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehavior.java
index 6e47293a2..2c5599c90 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehavior.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuBehavior.java
@@ -114,7 +114,7 @@ public class ShowHabitMenuBehavior
if (habit.isNumerical())
value = (int) (1000 + 250 * random.nextGaussian() * strength / 100) * 1000;
- habit.getRepetitions().add(new Repetition(DateUtils.getToday().minus(i), value));
+ habit.getRepetitions().setValue(DateUtils.getToday().minus(i), value);
}
habit.invalidateNewerThan(Timestamp.ZERO);
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 0c534cd5b..fae0b53d4 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
@@ -29,6 +29,8 @@ import org.jetbrains.annotations.*;
import javax.inject.*;
+import static org.isoron.uhabits.core.models.Checkmark.*;
+
public class WidgetBehavior
{
private HabitList habitList;
@@ -57,57 +59,42 @@ public class WidgetBehavior
public void onAddRepetition(@NonNull Habit habit, Timestamp timestamp)
{
notificationTray.cancel(habit);
- Repetition rep = habit.getRepetitions().getByTimestamp(timestamp);
- if (rep != null) return;
- performToggle(habit, timestamp, Checkmark.YES_MANUAL);
+ setValue(habit, timestamp, YES_MANUAL);
}
public void onRemoveRepetition(@NonNull Habit habit, Timestamp timestamp)
{
notificationTray.cancel(habit);
- Repetition rep = habit.getRepetitions().getByTimestamp(timestamp);
- if (rep == null) return;
- performToggle(habit, timestamp, Checkmark.NO);
+ setValue(habit, timestamp, NO);
}
public void onToggleRepetition(@NonNull Habit habit, Timestamp timestamp)
{
- Repetition previous = habit.getRepetitions().getByTimestamp(timestamp);
- if(previous == null)
- {
- performToggle(habit, timestamp, Checkmark.YES_MANUAL);
- }
+ int currentValue = habit.getRepetitions().getValue(timestamp);
+ int newValue;
+ if(preferences.isSkipEnabled())
+ newValue = Repetition.nextToggleValueWithSkip(currentValue);
else
- {
- int value;
- if(preferences.isSkipEnabled())
- value = Repetition.nextToggleValueWithSkip(previous.getValue());
- else
- value = Repetition.nextToggleValueWithoutSkip(previous.getValue());
- performToggle(habit, timestamp, value);
- }
- }
-
- private void performToggle(@NonNull Habit habit, Timestamp timestamp, int value)
- {
- commandRunner.execute(
- new CreateRepetitionCommand(habitList, habit, timestamp, value),
- habit.getId());
- }
-
- public void setNumericValue(@NonNull Habit habit, Timestamp timestamp, int newValue) {
- commandRunner.execute(
- new CreateRepetitionCommand(habitList, habit, timestamp, newValue),
- habit.getId());
+ newValue = Repetition.nextToggleValueWithoutSkip(currentValue);
+ setValue(habit, timestamp, newValue);
+ notificationTray.cancel(habit);
}
public void onIncrement(@NotNull Habit habit, @NotNull Timestamp timestamp, int amount) {
int currentValue = habit.getCheckmarks().getValues(timestamp, timestamp)[0];
- setNumericValue(habit, timestamp, currentValue + amount);
+ setValue(habit, timestamp, currentValue + amount);
+ notificationTray.cancel(habit);
}
public void onDecrement(@NotNull Habit habit, @NotNull Timestamp timestamp, int amount) {
int currentValue = habit.getCheckmarks().getValues(timestamp, timestamp)[0];
- setNumericValue(habit, timestamp, currentValue - amount);
+ setValue(habit, timestamp, currentValue - amount);
+ notificationTray.cancel(habit);
+ }
+
+ public void setValue(@NonNull Habit habit, Timestamp timestamp, int newValue) {
+ commandRunner.execute(
+ new CreateRepetitionCommand(habitList, habit, timestamp, newValue),
+ habit.getId());
}
}
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 8c95ff082..9092f127a 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
@@ -30,6 +30,7 @@ import java.util.*;
import static junit.framework.TestCase.assertFalse;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.*;
+import static org.isoron.uhabits.core.models.Checkmark.*;
import static org.isoron.uhabits.core.models.Frequency.*;
import static org.junit.Assert.assertTrue;
@@ -54,9 +55,9 @@ public class ImportTest extends BaseUnitTest
assertThat(habit.getName(), equalTo("Breed dragons"));
assertThat(habit.getDescription(), equalTo("with love and fire"));
assertThat(habit.getFrequency(), equalTo(Frequency.DAILY));
- assertTrue(containsRepetition(habit, 2016, 3, 18));
- assertTrue(containsRepetition(habit, 2016, 3, 19));
- assertFalse(containsRepetition(habit, 2016, 3, 20));
+ assertTrue(isChecked(habit, 2016, 3, 18));
+ assertTrue(isChecked(habit, 2016, 3, 19));
+ assertFalse(isChecked(habit, 2016, 3, 20));
}
@Test
@@ -69,9 +70,9 @@ public class ImportTest extends BaseUnitTest
Habit habit = habitList.getByPosition(0);
assertThat(habit.getName(), equalTo("Wake up early"));
assertThat(habit.getFrequency(), equalTo(THREE_TIMES_PER_WEEK));
- assertTrue(containsRepetition(habit, 2016, 3, 14));
- assertTrue(containsRepetition(habit, 2016, 3, 16));
- assertFalse(containsRepetition(habit, 2016, 3, 17));
+ assertTrue(isChecked(habit, 2016, 3, 14));
+ assertTrue(isChecked(habit, 2016, 3, 16));
+ assertFalse(isChecked(habit, 2016, 3, 17));
}
@Test
@@ -85,10 +86,10 @@ public class ImportTest extends BaseUnitTest
assertThat(habit.getName(), equalTo("Wake up early"));
assertThat(habit.getFrequency(), equalTo(THREE_TIMES_PER_WEEK));
assertFalse(habit.hasReminder());
- assertFalse(containsRepetition(habit, 2015, 12, 31));
- assertTrue(containsRepetition(habit, 2016, 1, 18));
- assertTrue(containsRepetition(habit, 2016, 1, 28));
- assertFalse(containsRepetition(habit, 2016, 3, 10));
+ assertFalse(isChecked(habit, 2015, 12, 31));
+ assertTrue(isChecked(habit, 2016, 1, 18));
+ assertTrue(isChecked(habit, 2016, 1, 28));
+ assertFalse(isChecked(habit, 2016, 3, 10));
habit = habitList.getByPosition(2);
assertThat(habit.getName(), equalTo("brush teeth"));
@@ -111,17 +112,18 @@ public class ImportTest extends BaseUnitTest
Habit h = habitList.getByPosition(2);
assertThat(h.getName(), equalTo("Vegan"));
- assertTrue(containsRepetition(h, 2016, 1, 24));
- assertTrue(containsRepetition(h, 2016, 2, 5));
- assertTrue(containsRepetition(h, 2016, 3, 18));
- assertFalse(containsRepetition(h, 2016, 3, 14));
+ assertTrue(isChecked(h, 2016, 1, 24));
+ assertTrue(isChecked(h, 2016, 2, 5));
+ assertTrue(isChecked(h, 2016, 3, 18));
+ assertFalse(isChecked(h, 2016, 3, 14));
}
- private boolean containsRepetition(Habit h, int year, int month, int day)
+ private boolean isChecked(Habit h, int year, int month, int day)
{
GregorianCalendar date = DateUtils.getStartOfTodayCalendar();
date.set(year, month - 1, day);
- return h.getRepetitions().containsTimestamp(new Timestamp(date));
+ Timestamp timestamp = new Timestamp(date);
+ return h.getRepetitions().getValue(timestamp) == YES_MANUAL;
}
private void importFromFile(String assetFilename) throws IOException
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..54787bed1 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
@@ -80,14 +80,14 @@ public class CheckmarkListTest extends BaseUnitTest
intervals.add(new CheckmarkList.Interval(day(2), day(2), day(1)));
List expected = new ArrayList<>();
- expected.add(new Checkmark(day(0), NO));
+ expected.add(new Checkmark(day(0), UNKNOWN));
expected.add(new Checkmark(day(1), YES_MANUAL));
expected.add(new Checkmark(day(2), YES_MANUAL));
- expected.add(new Checkmark(day(3), NO));
+ expected.add(new Checkmark(day(3), UNKNOWN));
expected.add(new Checkmark(day(4), YES_AUTO));
expected.add(new Checkmark(day(5), YES_MANUAL));
expected.add(new Checkmark(day(6), YES_AUTO));
- expected.add(new Checkmark(day(7), NO));
+ expected.add(new Checkmark(day(7), UNKNOWN));
expected.add(new Checkmark(day(8), YES_AUTO));
expected.add(new Checkmark(day(9), YES_AUTO));
expected.add(new Checkmark(day(10), YES_MANUAL));
@@ -220,9 +220,9 @@ public class CheckmarkListTest extends BaseUnitTest
travelInTime(3);
int[] expectedValues = {
- NO,
- NO,
- NO,
+ UNKNOWN,
+ UNKNOWN,
+ UNKNOWN,
YES_MANUAL,
NO,
YES_AUTO,
@@ -296,7 +296,7 @@ public class CheckmarkListTest extends BaseUnitTest
assertThat(checkmarks.getTodayValue(), equalTo(YES_MANUAL));
travelInTime(1);
- assertThat(checkmarks.getTodayValue(), equalTo(NO));
+ assertThat(checkmarks.getTodayValue(), equalTo(UNKNOWN));
}
@Test
@@ -320,12 +320,12 @@ public class CheckmarkListTest extends BaseUnitTest
YES_AUTO,
YES_MANUAL,
YES_MANUAL,
- NO,
- NO,
- NO,
- NO,
- NO,
- NO
+ UNKNOWN,
+ UNKNOWN,
+ UNKNOWN,
+ UNKNOWN,
+ UNKNOWN,
+ UNKNOWN
};
int[] actualValues = nonDailyHabit.getCheckmarks().getValues(from, to);
@@ -475,7 +475,7 @@ public class CheckmarkListTest extends BaseUnitTest
assertThat(checkmarks.getThisMonthValue(), equalTo(1006));
DateUtils.setFixedLocalTime(unixTime(2000, MAY, 1));
- assertThat(checkmarks.getTodayValue(), equalTo(0));
+ assertThat(checkmarks.getTodayValue(), equalTo(UNKNOWN));
assertThat(checkmarks.getThisWeekValue(SATURDAY), equalTo(0));
assertThat(checkmarks.getThisMonthValue(), equalTo(0));
}
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 7cab255bc..226b0a00e 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
@@ -89,7 +89,7 @@ public class HabitTest extends BaseUnitTest
{
Habit h = modelFactory.buildHabit();
assertFalse(h.isCompletedToday());
- h.getRepetitions().toggle(getToday());
+ h.getRepetitions().setValue(getToday(), Checkmark.YES_MANUAL);
assertTrue(h.isCompletedToday());
}
@@ -102,19 +102,19 @@ public class HabitTest extends BaseUnitTest
h.setTargetValue(100.0);
assertFalse(h.isCompletedToday());
- h.getRepetitions().toggle(getToday(), 200_000);
+ h.getRepetitions().setValue(getToday(), 200_000);
assertTrue(h.isCompletedToday());
- h.getRepetitions().toggle(getToday(), 100_000);
+ h.getRepetitions().setValue(getToday(), 100_000);
assertTrue(h.isCompletedToday());
- h.getRepetitions().toggle(getToday(), 50_000);
+ h.getRepetitions().setValue(getToday(), 50_000);
assertFalse(h.isCompletedToday());
h.setTargetType(Habit.AT_MOST);
- h.getRepetitions().toggle(getToday(), 200_000);
+ h.getRepetitions().setValue(getToday(), 200_000);
assertFalse(h.isCompletedToday());
- h.getRepetitions().toggle(getToday(), 100_000);
+ h.getRepetitions().setValue(getToday(), 100_000);
assertTrue(h.isCompletedToday());
- h.getRepetitions().toggle(getToday(), 50_000);
+ h.getRepetitions().setValue(getToday(), 50_000);
assertTrue(h.isCompletedToday());
}
diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/RepetitionListTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/RepetitionListTest.java
index 920ebed4f..add47d86a 100644
--- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/RepetitionListTest.java
+++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/RepetitionListTest.java
@@ -28,11 +28,9 @@ import org.junit.*;
import java.util.*;
import static java.util.Calendar.*;
-import static junit.framework.TestCase.assertFalse;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.isoron.uhabits.core.models.Checkmark.*;
import static org.mockito.Mockito.*;
public class RepetitionListTest extends BaseUnitTest
@@ -60,11 +58,11 @@ public class RepetitionListTest extends BaseUnitTest
today = DateUtils.getToday();
- reps.toggle(today.minus(3));
- reps.toggle(today.minus(2));
- reps.toggle(today);
- reps.toggle(today.minus(7));
- reps.toggle(today.minus(5));
+ reps.setValue(today.minus(3), YES_MANUAL);
+ reps.setValue(today.minus(2), YES_MANUAL);
+ reps.setValue(today, YES_MANUAL);
+ reps.setValue(today.minus(7), YES_MANUAL);
+ reps.setValue(today.minus(5), YES_MANUAL);
listener = mock(ModelObservable.Listener.class);
reps.getObservable().addListener(listener);
@@ -78,17 +76,6 @@ public class RepetitionListTest extends BaseUnitTest
super.tearDown();
}
- @Test
- public void test_contains()
- {
- assertTrue(reps.containsTimestamp(today));
- assertTrue(reps.containsTimestamp(today.minus(2)));
- assertTrue(reps.containsTimestamp(today.minus(3)));
-
- assertFalse(reps.containsTimestamp(today.minus(1)));
- assertFalse(reps.containsTimestamp(today.minus(4)));
- }
-
@Test
public void test_getOldest()
{
@@ -126,7 +113,7 @@ public class RepetitionListTest extends BaseUnitTest
// Leave the month of March empty, to check that it returns null
if (month == MARCH) continue;
- reps.toggle(new Timestamp(day));
+ reps.setValue(new Timestamp(day), YES_MANUAL);
// Repetitions in December should not be counted
if (month == DECEMBER) continue;
@@ -155,32 +142,22 @@ public class RepetitionListTest extends BaseUnitTest
}
@Test
- public void test_toggle()
+ public void test_setValue()
{
- assertTrue(reps.containsTimestamp(today));
- reps.toggle(today);
- assertFalse(reps.containsTimestamp(today));
- verify(listener).onModelChange();
- reset(listener);
-
- assertFalse(reps.containsTimestamp(today.minus(1)));
- reps.toggle(today.minus(1));
- assertTrue(reps.containsTimestamp(today.minus(1)));
- verify(listener).onModelChange();
+ assertThat(reps.getValue(today), equalTo(YES_MANUAL));
+ reps.setValue(today, NO);
+ assertThat(reps.getValue(today), equalTo(NO));
+ verify(listener, times(2)).onModelChange();
reset(listener);
habit.setType(Habit.NUMBER_HABIT);
- reps.toggle(today, 100);
- Repetition check = reps.getByTimestamp(today);
- assertNotNull(check);
- assertThat(check.getValue(), equalTo(100));
- verify(listener).onModelChange();
+ reps.setValue(today, 100);
+ assertThat(reps.getValue(today), equalTo(100));
+ verify(listener, times(2)).onModelChange();
reset(listener);
- reps.toggle(today, 500);
- check = reps.getByTimestamp(today);
- assertNotNull(check);
- assertThat(check.getValue(), equalTo(500));
+ reps.setValue(today, 500);
+ assertThat(reps.getValue(today), equalTo(500));
verify(listener, times(2)).onModelChange();
reset(listener);
}
diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java
index 576aef0d6..d74925a3b 100644
--- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java
+++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/models/ScoreListTest.java
@@ -49,7 +49,7 @@ public class ScoreListTest extends BaseUnitTest
@Test
public void test_getAll()
{
- toggle(0, 20);
+ check(0, 20);
double expectedValues[] = {
0.655747,
@@ -82,7 +82,7 @@ public class ScoreListTest extends BaseUnitTest
@Test
public void test_getTodayValue()
{
- toggle(0, 20);
+ check(0, 20);
double actual = habit.getScores().getTodayValue();
assertThat(actual, closeTo(0.655747, E));
}
@@ -90,7 +90,7 @@ public class ScoreListTest extends BaseUnitTest
@Test
public void test_getValue()
{
- toggle(0, 20);
+ check(0, 20);
double expectedValues[] = {
0.655747,
@@ -124,7 +124,7 @@ public class ScoreListTest extends BaseUnitTest
@Test
public void test_getValueWithSkip()
{
- toggle(0, 20);
+ check(0, 20);
addSkip(5);
addSkip(10);
addSkip(11);
@@ -161,7 +161,7 @@ public class ScoreListTest extends BaseUnitTest
@Test
public void test_getValueWithSkip2()
{
- toggle(5);
+ check(5);
addSkip(4);
double[] expectedValues = {
@@ -180,7 +180,7 @@ public class ScoreListTest extends BaseUnitTest
@Test
public void test_getValues()
{
- toggle(0, 20);
+ check(0, 20);
Timestamp today = DateUtils.getToday();
Timestamp from = today.minus(4);
@@ -214,7 +214,7 @@ public class ScoreListTest extends BaseUnitTest
values.add(NO);
values.add(NO);
}
- toggle(values);
+ check(values);
assertThat(habit.getScores().getTodayValue(), closeTo(2/3.0, E));
// Missing 2 repetitions out of 4 per week, the score should converge to 50%
@@ -249,7 +249,7 @@ public class ScoreListTest extends BaseUnitTest
values.add(NO);
values.add(YES_MANUAL);
}
- toggle(values);
+ check(values);
assertThat(habit.getScores().getTodayValue(), closeTo(1.0, 1e-3));
}
@@ -259,18 +259,18 @@ public class ScoreListTest extends BaseUnitTest
// Daily habits should achieve at least 99% in 3 months
habit = fixtures.createEmptyHabit();
habit.setFrequency(Frequency.DAILY);
- for (int i = 0; i < 90; i++) toggle(i);
+ for (int i = 0; i < 90; i++) check(i);
assertThat(habit.getScores().getTodayValue(), greaterThan(0.99));
// Weekly habits should achieve at least 99% in 9 months
habit = fixtures.createEmptyHabit();
habit.setFrequency(Frequency.WEEKLY);
- for (int i = 0; i < 39; i++) toggle(7 * i);
+ for (int i = 0; i < 39; i++) check(7 * i);
assertThat(habit.getScores().getTodayValue(), greaterThan(0.99));
// Monthly habits should achieve at least 99% in 18 months
habit.setFrequency(new Frequency(1, 30));
- for (int i = 0; i < 18; i++) toggle(30 * i);
+ for (int i = 0; i < 18; i++) check(30 * i);
assertThat(habit.getScores().getTodayValue(), greaterThan(0.99));
}
@@ -292,7 +292,7 @@ public class ScoreListTest extends BaseUnitTest
{
assertThat(habit.getScores().getTodayValue(), closeTo(0.0, E));
- toggle(0, 2);
+ check(0, 2);
assertThat(habit.getScores().getTodayValue(), closeTo(0.101149, E));
habit.setFrequency(new Frequency(1, 2));
@@ -324,36 +324,36 @@ public class ScoreListTest extends BaseUnitTest
assertThat(writer.toString(), equalTo(expectedCSV));
}
- private void toggle(final int offset)
+ private void check(final int offset)
{
RepetitionList reps = habit.getRepetitions();
Timestamp today = DateUtils.getToday();
- reps.toggle(today.minus(offset));
+ reps.setValue(today.minus(offset), YES_MANUAL);
}
- private void toggle(final int from, final int to)
+ private void check(final int from, final int to)
{
RepetitionList reps = habit.getRepetitions();
Timestamp today = DateUtils.getToday();
for (int i = from; i < to; i++)
- reps.toggle(today.minus(i));
+ reps.setValue(today.minus(i), YES_MANUAL);
}
- private void toggle(ArrayList values)
+ private void check(ArrayList values)
{
RepetitionList reps = habit.getRepetitions();
Timestamp today = DateUtils.getToday();
for (int i = 0; i < values.size(); i++)
if (values.get(i) == YES_MANUAL)
- reps.toggle(today.minus(i));
+ reps.setValue(today.minus(i), YES_MANUAL);
}
private void addSkip(final int day)
{
RepetitionList reps = habit.getRepetitions();
Timestamp today = DateUtils.getToday();
- reps.toggle(today.minus(day), Checkmark.SKIP);
+ reps.setValue(today.minus(day), Checkmark.SKIP);
}
private void checkScoreValues(double[] expectedValues)
diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/widgets/WidgetBehaviorTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/widgets/WidgetBehaviorTest.java
new file mode 100644
index 000000000..eea405019
--- /dev/null
+++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/widgets/WidgetBehaviorTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.widgets;
+
+import org.isoron.uhabits.core.*;
+import org.isoron.uhabits.core.commands.*;
+import org.isoron.uhabits.core.models.*;
+import org.isoron.uhabits.core.preferences.*;
+import org.isoron.uhabits.core.ui.*;
+import org.junit.*;
+
+import java.util.*;
+
+import static org.isoron.uhabits.core.models.Checkmark.*;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+public class WidgetBehaviorTest extends BaseUnitTest
+{
+ private NotificationTray notificationTray;
+
+ private CommandRunner commandRunner;
+
+ private Preferences preferences;
+
+ private WidgetBehavior behavior;
+
+ private Habit habit;
+
+ private Timestamp timestamp = new Timestamp(0L);
+
+ @Before
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ habit = fixtures.createEmptyHabit();
+ commandRunner = mock(CommandRunner.class);
+ notificationTray = mock(NotificationTray.class);
+ preferences = mock(Preferences.class);
+ behavior = new WidgetBehavior(habitList, commandRunner, notificationTray, preferences);
+ }
+
+ @Test
+ public void testOnAddRepetition()
+ {
+ behavior.onAddRepetition(habit, timestamp);
+ verify(commandRunner).execute(
+ new CreateRepetitionCommand(habitList, habit, timestamp, YES_MANUAL),
+ habit.id);
+ verify(notificationTray).cancel(habit);
+ verifyZeroInteractions(preferences);
+ }
+
+ @Test
+ public void testOnRemoveRepetition()
+ {
+ behavior.onRemoveRepetition(habit, timestamp);
+ verify(commandRunner).execute(
+ new CreateRepetitionCommand(habitList, habit, timestamp, NO),
+ habit.id);
+ verify(notificationTray).cancel(habit);
+ verifyZeroInteractions(preferences);
+ }
+
+ @Test
+ public void testOnToggleRepetition()
+ {
+ for (boolean skipEnabled : Arrays.asList(true, false))
+ for (int currentValue : Arrays.asList(NO, YES_MANUAL, YES_AUTO, SKIP))
+ {
+ when(preferences.isSkipEnabled()).thenReturn(skipEnabled);
+
+ int nextValue;
+ if(skipEnabled) nextValue = Repetition.nextToggleValueWithSkip(currentValue);
+ else nextValue = Repetition.nextToggleValueWithoutSkip(currentValue);
+
+ habit.getRepetitions().setValue(timestamp, currentValue);
+ behavior.onToggleRepetition(habit, timestamp);
+ verify(preferences).isSkipEnabled();
+ verify(commandRunner).execute(
+ new CreateRepetitionCommand(habitList, habit, timestamp, nextValue),
+ habit.id);
+ verify(notificationTray).cancel(habit);
+ reset(preferences, commandRunner, notificationTray);
+ }
+ }
+
+ @Test
+ public void testOnIncrement()
+ {
+ habit = fixtures.createNumericalHabit();
+ habit.getRepetitions().setValue(timestamp, 500);
+
+ behavior.onIncrement(habit, timestamp, 100);
+ verify(commandRunner).execute(
+ new CreateRepetitionCommand(habitList, habit, timestamp, 600),
+ habit.id);
+ verify(notificationTray).cancel(habit);
+ verifyZeroInteractions(preferences);
+ }
+
+ @Test
+ public void testOnDecrement()
+ {
+ habit = fixtures.createNumericalHabit();
+ habit.getRepetitions().setValue(timestamp, 500);
+
+ behavior.onDecrement(habit, timestamp, 100);
+ verify(commandRunner).execute(
+ new CreateRepetitionCommand(habitList, habit, timestamp, 400),
+ habit.id);
+ verify(notificationTray).cancel(habit);
+ verifyZeroInteractions(preferences);
+ }
+}
diff --git a/settings.gradle-fdroid b/settings.gradle-fdroid
new file mode 100644
index 000000000..0cc822abf
--- /dev/null
+++ b/settings.gradle-fdroid
@@ -0,0 +1,3 @@
+# Dummy settings.gradle file that helps F-Droid locate the Android project
+# metadata (app description and screenshots). See #670 for more details.
+include ':android:uhabits-android'