mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-07 01:28:52 -06:00
Merge branch 'dev' into feature/sync
This commit is contained in:
@@ -23,6 +23,8 @@ import androidx.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Command to toggle a repetition.
|
||||
*/
|
||||
@@ -31,20 +33,19 @@ public class CreateRepetitionCommand extends Command
|
||||
@NonNull
|
||||
final Habit habit;
|
||||
|
||||
private HabitList habitList;
|
||||
@NonNull
|
||||
final HabitList habitList;
|
||||
|
||||
@NonNull
|
||||
final Timestamp timestamp;
|
||||
|
||||
final int value;
|
||||
|
||||
@Nullable
|
||||
Repetition previousRep;
|
||||
|
||||
@Nullable
|
||||
Repetition newRep;
|
||||
int previousValue;
|
||||
|
||||
public CreateRepetitionCommand(@NonNull HabitList habitList,
|
||||
@NonNull Habit habit,
|
||||
Timestamp timestamp,
|
||||
@NonNull Timestamp timestamp,
|
||||
int value)
|
||||
{
|
||||
this.habitList = habitList;
|
||||
@@ -57,18 +58,8 @@ public class CreateRepetitionCommand extends Command
|
||||
public void execute()
|
||||
{
|
||||
RepetitionList reps = habit.getRepetitions();
|
||||
|
||||
previousRep = reps.getByTimestamp(timestamp);
|
||||
if (previousRep != null) reps.remove(previousRep);
|
||||
|
||||
if (value > 0)
|
||||
{
|
||||
newRep = new Repetition(timestamp, value);
|
||||
reps.add(newRep);
|
||||
}
|
||||
|
||||
habit.invalidateNewerThan(timestamp);
|
||||
habitList.update(habit);
|
||||
previousValue = reps.getValue(timestamp);
|
||||
reps.setValue(timestamp, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -87,9 +78,7 @@ public class CreateRepetitionCommand extends Command
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
if(newRep != null) habit.getRepetitions().remove(newRep);
|
||||
if (previousRep != null) habit.getRepetitions().add(previousRep);
|
||||
habit.invalidateNewerThan(timestamp);
|
||||
habit.getRepetitions().setValue(timestamp, previousValue);
|
||||
}
|
||||
|
||||
public static class Record
|
||||
@@ -129,4 +118,34 @@ public class CreateRepetitionCommand extends Command
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CreateRepetitionCommand that = (CreateRepetitionCommand) o;
|
||||
return value == that.value &&
|
||||
habit.equals(that.habit) &&
|
||||
habitList.equals(that.habitList) &&
|
||||
timestamp.equals(that.timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(habit, habitList, timestamp, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "CreateRepetitionCommand{" +
|
||||
"habit=" + habit +
|
||||
", habitList=" + habitList +
|
||||
", timestamp=" + timestamp +
|
||||
", value=" + value +
|
||||
", previousValue=" + previousValue +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import static org.isoron.uhabits.core.models.Checkmark.*;
|
||||
|
||||
|
||||
/**
|
||||
* Class that imports data from HabitBull CSV files.
|
||||
@@ -93,8 +95,7 @@ public class HabitBullCSVImporter extends AbstractImporter
|
||||
map.put(name, h);
|
||||
}
|
||||
|
||||
if (!h.getRepetitions().containsTimestamp(timestamp))
|
||||
h.getRepetitions().toggle(timestamp);
|
||||
h.getRepetitions().setValue(timestamp, YES_MANUAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import static org.isoron.uhabits.core.models.Checkmark.*;
|
||||
|
||||
/**
|
||||
* Class that imports database files exported by Rewire.
|
||||
*/
|
||||
@@ -165,7 +167,7 @@ public class RewireDBImporter extends AbstractImporter
|
||||
GregorianCalendar cal = DateUtils.getStartOfTodayCalendar();
|
||||
cal.set(year, month - 1, day);
|
||||
|
||||
habit.getRepetitions().toggle(new Timestamp(cal));
|
||||
habit.getRepetitions().setValue(new Timestamp(cal), YES_MANUAL);
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -30,6 +30,8 @@ import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import static org.isoron.uhabits.core.models.Checkmark.*;
|
||||
|
||||
/**
|
||||
* Class that imports data from database files exported by Tickmate.
|
||||
*/
|
||||
@@ -100,7 +102,7 @@ public class TickmateDBImporter extends AbstractImporter
|
||||
GregorianCalendar cal = DateUtils.getStartOfTodayCalendar();
|
||||
cal.set(year, month, day);
|
||||
|
||||
habit.getRepetitions().toggle(new Timestamp(cal));
|
||||
habit.getRepetitions().setValue(new Timestamp(cal), YES_MANUAL);
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -43,27 +43,32 @@ import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle;
|
||||
public final class Checkmark
|
||||
{
|
||||
/**
|
||||
* Indicates that there was an explicit skip at the timestamp.
|
||||
* Checkmark value indicating that the habit is not applicable for this timestamp.
|
||||
*/
|
||||
public static final int SKIP = 3;
|
||||
|
||||
/**
|
||||
* Indicates that there was a repetition at the timestamp.
|
||||
* Checkmark value indicating that the user has performed the habit at this timestamp.
|
||||
*/
|
||||
public static final int YES_MANUAL = 2;
|
||||
|
||||
/**
|
||||
* Indicates that there was no repetition at the timestamp, but one was not
|
||||
* expected in any case, due to the frequency of the habit.
|
||||
* Checkmark value indicating that the user did not perform the habit, but they were not
|
||||
* expected to, because of the frequency of the habit.
|
||||
*/
|
||||
public static final int YES_AUTO = 1;
|
||||
|
||||
/**
|
||||
* Indicates that there was no repetition at the timestamp, even though a
|
||||
* repetition was expected.
|
||||
* Checkmark value indicating that the user did not perform the habit, even though they were
|
||||
* expected to perform it.
|
||||
*/
|
||||
public static final int NO = 0;
|
||||
|
||||
/**
|
||||
* Checkmark value indicating that no data is available for the given timestamp.
|
||||
*/
|
||||
public static final int UNKNOWN = -1;
|
||||
|
||||
private final Timestamp timestamp;
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,7 +62,7 @@ public abstract class CheckmarkList
|
||||
int nDays = begin.daysUntil(today) + 1;
|
||||
List<Checkmark> 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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -80,14 +80,14 @@ public class CheckmarkListTest extends BaseUnitTest
|
||||
intervals.add(new CheckmarkList.Interval(day(2), day(2), day(1)));
|
||||
|
||||
List<Checkmark> 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));
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<Integer> values)
|
||||
private void check(ArrayList<Integer> 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)
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2020 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user