Refactor Habit class

pull/145/head
Alinson S. Xavier 9 years ago
parent b13f2b4228
commit ec4a381d70

@ -19,8 +19,7 @@
package org.isoron.uhabits; package org.isoron.uhabits;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.HabitList;
import org.isoron.uhabits.utils.DateUtils; import org.isoron.uhabits.utils.DateUtils;
public class HabitFixtures public class HabitFixtures
@ -42,8 +41,7 @@ public class HabitFixtures
habit.setName("Meditate"); habit.setName("Meditate");
habit.setDescription("Did you meditate this morning?"); habit.setDescription("Did you meditate this morning?");
habit.setColor(3); habit.setColor(3);
habit.setFreqNum(1); habit.setFrequency(Frequency.DAILY);
habit.setFreqDen(1);
habitList.add(habit); habitList.add(habit);
return habit; return habit;
} }
@ -51,8 +49,7 @@ public class HabitFixtures
public Habit createLongHabit() public Habit createLongHabit()
{ {
Habit habit = createEmptyHabit(); Habit habit = createEmptyHabit();
habit.setFreqNum(3); habit.setFrequency(new Frequency(3, 7));
habit.setFreqDen(7);
habit.setColor(4); habit.setColor(4);
long day = DateUtils.millisecondsInOneDay; long day = DateUtils.millisecondsInOneDay;
@ -72,8 +69,7 @@ public class HabitFixtures
Habit habit = new Habit(); Habit habit = new Habit();
habit.setName("Wake up early"); habit.setName("Wake up early");
habit.setDescription("Did you wake up before 6am?"); habit.setDescription("Did you wake up before 6am?");
habit.setFreqNum(2); habit.setFrequency(new Frequency(2, 3));
habit.setFreqDen(3);
habitList.add(habit); habitList.add(habit);
long timestamp = DateUtils.getStartOfToday(); long timestamp = DateUtils.getStartOfToday();

@ -41,6 +41,7 @@ import static org.junit.Assert.*;
public class ImportTest extends BaseAndroidTest public class ImportTest extends BaseAndroidTest
{ {
private File baseDir; private File baseDir;
private Context context; private Context context;
@Before @Before
@ -52,49 +53,40 @@ public class ImportTest extends BaseAndroidTest
fixtures.purgeHabits(habitList); fixtures.purgeHabits(habitList);
context = InstrumentationRegistry.getInstrumentation().getContext(); context = InstrumentationRegistry.getInstrumentation().getContext();
baseDir = FileUtils.getFilesDir("Backups"); baseDir = FileUtils.getFilesDir("Backups");
if(baseDir == null) fail("baseDir should not be null"); if (baseDir == null) fail("baseDir should not be null");
}
private void copyAssetToFile(String assetPath, File dst) throws IOException
{
InputStream in = context.getAssets().open(assetPath);
FileUtils.copy(in, dst);
} }
private void importFromFile(String assetFilename) throws IOException @Test
public void testHabitBullCSV() throws IOException
{ {
File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename)); importFromFile("habitbull.csv");
copyAssetToFile(assetFilename, file);
assertTrue(file.exists());
assertTrue(file.canRead());
GenericImporter importer = new GenericImporter();
assertThat(importer.canHandle(file), is(true));
importer.importHabitsFromFile(file); List<Habit> habits = habitList.getAll(true);
} assertThat(habits.size(), equalTo(4));
private boolean containsRepetition(Habit h, int year, int month, int day) Habit habit = habits.get(0);
{ assertThat(habit.getName(), equalTo("Breed dragons"));
GregorianCalendar date = DateUtils.getStartOfTodayCalendar(); assertThat(habit.getDescription(), equalTo("with love and fire"));
date.set(year, month - 1, day); assertThat(habit.getFrequency(), equalTo(Frequency.DAILY));
return h.getRepetitions().containsTimestamp(date.getTimeInMillis()); assertTrue(containsRepetition(habit, 2016, 3, 18));
assertTrue(containsRepetition(habit, 2016, 3, 19));
assertFalse(containsRepetition(habit, 2016, 3, 20));
} }
@Test @Test
public void testTickmateDB() throws IOException public void testLoopDB() throws IOException
{ {
importFromFile("tickmate.db"); importFromFile("loop.db");
List<Habit> habits = habitList.getAll(true); List<Habit> habits = habitList.getAll(true);
assertThat(habits.size(), equalTo(3)); assertThat(habits.size(), equalTo(9));
Habit h = habits.get(0); Habit habit = habits.get(0);
assertThat(h.getName(), equalTo("Vegan")); assertThat(habit.getName(), equalTo("Wake up early"));
assertTrue(containsRepetition(h, 2016, 1, 24)); assertThat(habit.getFrequency(), equalTo(Frequency.THREE_TIMES_PER_WEEK));
assertTrue(containsRepetition(h, 2016, 2, 5)); assertTrue(containsRepetition(habit, 2016, 3, 14));
assertTrue(containsRepetition(h, 2016, 3, 18)); assertTrue(containsRepetition(habit, 2016, 3, 16));
assertFalse(containsRepetition(h, 2016, 3, 14)); assertFalse(containsRepetition(habit, 2016, 3, 17));
} }
@Test @Test
@ -107,8 +99,8 @@ public class ImportTest extends BaseAndroidTest
Habit habit = habits.get(0); Habit habit = habits.get(0);
assertThat(habit.getName(), equalTo("Wake up early")); assertThat(habit.getName(), equalTo("Wake up early"));
assertThat(habit.getFreqNum(), equalTo(3)); assertThat(habit.getFrequency(),
assertThat(habit.getFreqDen(), equalTo(7)); equalTo(Frequency.THREE_TIMES_PER_WEEK));
assertFalse(habit.hasReminder()); assertFalse(habit.hasReminder());
assertFalse(containsRepetition(habit, 2015, 12, 31)); assertFalse(containsRepetition(habit, 2015, 12, 31));
assertTrue(containsRepetition(habit, 2016, 1, 18)); assertTrue(containsRepetition(habit, 2016, 1, 18));
@ -117,49 +109,58 @@ public class ImportTest extends BaseAndroidTest
habit = habits.get(1); habit = habits.get(1);
assertThat(habit.getName(), equalTo("brush teeth")); assertThat(habit.getName(), equalTo("brush teeth"));
assertThat(habit.getFreqNum(), equalTo(3)); assertThat(habit.getFrequency(),
assertThat(habit.getFreqDen(), equalTo(7)); equalTo(Frequency.THREE_TIMES_PER_WEEK));
assertThat(habit.hasReminder(), equalTo(true)); assertThat(habit.hasReminder(), equalTo(true));
Reminder reminder = habit.getReminder(); Reminder reminder = habit.getReminder();
assertThat(reminder.getHour(), equalTo(8)); assertThat(reminder.getHour(), equalTo(8));
assertThat(reminder.getMinute(), equalTo(0)); assertThat(reminder.getMinute(), equalTo(0));
boolean[] reminderDays = {false, true, true, true, true, true, false}; boolean[] reminderDays = { false, true, true, true, true, true, false };
assertThat(reminder.getDays(), equalTo(DateUtils.packWeekdayList(reminderDays))); assertThat(reminder.getDays(),
equalTo(DateUtils.packWeekdayList(reminderDays)));
} }
@Test @Test
public void testHabitBullCSV() throws IOException public void testTickmateDB() throws IOException
{ {
importFromFile("habitbull.csv"); importFromFile("tickmate.db");
List<Habit> habits = habitList.getAll(true); List<Habit> habits = habitList.getAll(true);
assertThat(habits.size(), equalTo(4)); assertThat(habits.size(), equalTo(3));
Habit habit = habits.get(0); Habit h = habits.get(0);
assertThat(habit.getName(), equalTo("Breed dragons")); assertThat(h.getName(), equalTo("Vegan"));
assertThat(habit.getDescription(), equalTo("with love and fire")); assertTrue(containsRepetition(h, 2016, 1, 24));
assertThat(habit.getFreqNum(), equalTo(1)); assertTrue(containsRepetition(h, 2016, 2, 5));
assertThat(habit.getFreqDen(), equalTo(1)); assertTrue(containsRepetition(h, 2016, 3, 18));
assertTrue(containsRepetition(habit, 2016, 3, 18)); assertFalse(containsRepetition(h, 2016, 3, 14));
assertTrue(containsRepetition(habit, 2016, 3, 19));
assertFalse(containsRepetition(habit, 2016, 3, 20));
} }
@Test private boolean containsRepetition(Habit h, int year, int month, int day)
public void testLoopDB() throws IOException
{ {
importFromFile("loop.db"); GregorianCalendar date = DateUtils.getStartOfTodayCalendar();
date.set(year, month - 1, day);
return h.getRepetitions().containsTimestamp(date.getTimeInMillis());
}
List<Habit> habits = habitList.getAll(true); private void copyAssetToFile(String assetPath, File dst) throws IOException
assertThat(habits.size(), equalTo(9)); {
InputStream in = context.getAssets().open(assetPath);
FileUtils.copy(in, dst);
}
Habit habit = habits.get(0); private void importFromFile(String assetFilename) throws IOException
assertThat(habit.getName(), equalTo("Wake up early")); {
assertThat(habit.getFreqNum(), equalTo(3)); File file =
assertThat(habit.getFreqDen(), equalTo(7)); new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
assertTrue(containsRepetition(habit, 2016, 3, 14)); copyAssetToFile(assetFilename, file);
assertTrue(containsRepetition(habit, 2016, 3, 16)); assertTrue(file.exists());
assertFalse(containsRepetition(habit, 2016, 3, 17)); assertTrue(file.canRead());
GenericImporter importer = new GenericImporter();
assertThat(importer.canHandle(file), is(true));
importer.importHabitsFromFile(file);
} }
} }

@ -56,7 +56,7 @@ public class SQLiteHabitListTest extends BaseAndroidTest
Habit h = new Habit(); Habit h = new Habit();
h.setName("habit " + i); h.setName("habit " + i);
h.setId((long) i); h.setId((long) i);
if (i % 2 == 0) h.setArchived(1); if (i % 2 == 0) h.setArchived(true);
HabitRecord record = new HabitRecord(); HabitRecord record = new HabitRecord();
record.copyFrom(h); record.copyFrom(h);

@ -67,7 +67,7 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest
RepetitionRecord record = getByTimestamp(today + day); RepetitionRecord record = getByTimestamp(today + day);
assertThat(record, is(nullValue())); assertThat(record, is(nullValue()));
Repetition rep = new Repetition(habit, today + day); Repetition rep = new Repetition(today + day);
habit.getRepetitions().add(rep); habit.getRepetitions().add(rep);
record = getByTimestamp(today + day); record = getByTimestamp(today + day);
@ -91,7 +91,6 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest
{ {
Repetition rep = repetitions.getByTimestamp(today); Repetition rep = repetitions.getByTimestamp(today);
assertThat(rep, is(not(nullValue()))); assertThat(rep, is(not(nullValue())));
assertThat(rep.getHabit(), equalTo(habit));
assertThat(rep.getTimestamp(), equalTo(today)); assertThat(rep.getTimestamp(), equalTo(today));
rep = repetitions.getByTimestamp(today - 2 * day); rep = repetitions.getByTimestamp(today - 2 * day);
@ -103,7 +102,6 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest
{ {
Repetition rep = repetitions.getOldest(); Repetition rep = repetitions.getOldest();
assertThat(rep, is(not(nullValue()))); assertThat(rep, is(not(nullValue())));
assertThat(rep.getHabit(), equalTo(habit));
assertThat(rep.getTimestamp(), equalTo(today - 120 * day)); assertThat(rep.getTimestamp(), equalTo(today - 120 * day));
} }

@ -92,9 +92,9 @@ public class SQLiteScoreListTest extends BaseAndroidTest
new Delete().from(ScoreRecord.class).execute(); new Delete().from(ScoreRecord.class).execute();
List<Score> list = new LinkedList<>(); List<Score> list = new LinkedList<>();
list.add(new Score(habit, today, 0)); list.add(new Score(today, 0));
list.add(new Score(habit, today - day, 0)); list.add(new Score(today - day, 0));
list.add(new Score(habit, today - 2 * day, 0)); list.add(new Score(today - 2 * day, 0));
scores.add(list); scores.add(list);

@ -47,14 +47,14 @@ public class ArchiveHabitsCommand extends Command
@Override @Override
public void execute() public void execute()
{ {
for(Habit h : habits) h.setArchived(1); for(Habit h : habits) h.setArchived(true);
habitList.update(habits); habitList.update(habits);
} }
@Override @Override
public void undo() public void undo()
{ {
for(Habit h : habits) h.setArchived(0); for(Habit h : habits) h.setArchived(false);
habitList.update(habits); habitList.update(habits);
} }

@ -19,12 +19,10 @@
package org.isoron.uhabits.commands; package org.isoron.uhabits.commands;
import org.isoron.uhabits.HabitsApplication; import org.isoron.uhabits.*;
import org.isoron.uhabits.R; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.HabitList;
import javax.inject.Inject; import javax.inject.*;
/** /**
* Command to modify a habit. * Command to modify a habit.
@ -40,7 +38,7 @@ public class EditHabitCommand extends Command
private long savedId; private long savedId;
private boolean hasIntervalChanged; private boolean hasFrequencyChanged;
public EditHabitCommand(Habit original, Habit modified) public EditHabitCommand(Habit original, Habit modified)
{ {
@ -53,9 +51,9 @@ public class EditHabitCommand extends Command
this.modified.copyFrom(modified); this.modified.copyFrom(modified);
this.original.copyFrom(original); this.original.copyFrom(original);
hasIntervalChanged = Frequency originalFreq = this.original.getFrequency();
(!this.original.getFreqDen().equals(this.modified.getFreqDen()) || Frequency modifiedFreq = this.modified.getFrequency();
!this.original.getFreqNum().equals(this.modified.getFreqNum())); hasFrequencyChanged = (!originalFreq.equals(modifiedFreq));
} }
@Override @Override
@ -95,7 +93,7 @@ public class EditHabitCommand extends Command
private void invalidateIfNeeded(Habit habit) private void invalidateIfNeeded(Habit habit)
{ {
if (hasIntervalChanged) if (hasFrequencyChanged)
{ {
habit.getCheckmarks().invalidateNewerThan(0); habit.getCheckmarks().invalidateNewerThan(0);
habit.getStreaks().invalidateNewerThan(0); habit.getStreaks().invalidateNewerThan(0);

@ -47,14 +47,14 @@ public class UnarchiveHabitsCommand extends Command
@Override @Override
public void execute() public void execute()
{ {
for(Habit h : habits) h.setArchived(0); for(Habit h : habits) h.setArchived(false);
habitList.update(habits); habitList.update(habits);
} }
@Override @Override
public void undo() public void undo()
{ {
for(Habit h : habits) h.setArchived(1); for(Habit h : habits) h.setArchived(true);
habitList.update(habits); habitList.update(habits);
} }

@ -24,7 +24,7 @@ import android.support.annotation.NonNull;
import com.activeandroid.ActiveAndroid; import com.activeandroid.ActiveAndroid;
import com.opencsv.CSVReader; import com.opencsv.CSVReader;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.DateUtils; import org.isoron.uhabits.utils.DateUtils;
import java.io.BufferedReader; import java.io.BufferedReader;
@ -94,8 +94,7 @@ public class HabitBullCSVImporter extends AbstractImporter
h = new Habit(); h = new Habit();
h.setName(name); h.setName(name);
h.setDescription(description); h.setDescription(description);
h.setFreqDen(1); h.setFrequency(Frequency.DAILY);
h.setFreqNum(1);
habitList.add(h); habitList.add(h);
habits.put(name, h); habits.put(name, h);
} }

@ -133,25 +133,30 @@ public class RewireDBImporter extends AbstractImporter
habit.setDescription(description); habit.setDescription(description);
int periods[] = { 7, 31, 365 }; int periods[] = { 7, 31, 365 };
int numerator, denominator;
switch (schedule) switch (schedule)
{ {
case 0: case 0:
habit.setFreqNum(activeDays.split(",").length); numerator = activeDays.split(",").length;
habit.setFreqDen(7); denominator = 7;
break; break;
case 1: case 1:
habit.setFreqNum(days); numerator = days;
habit.setFreqDen(periods[periodIndex]); denominator = (periods[periodIndex]);
break; break;
case 2: case 2:
habit.setFreqNum(1); numerator = 1;
habit.setFreqDen(repeatingCount); denominator = repeatingCount;
break; break;
default:
throw new IllegalStateException();
} }
habit.setFrequency(new Frequency(numerator, denominator));
habitList.add(habit); habitList.add(habit);
createReminder(db, habit, id); createReminder(db, habit, id);

@ -23,7 +23,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.DatabaseUtils; import org.isoron.uhabits.utils.DatabaseUtils;
import org.isoron.uhabits.utils.DateUtils; import org.isoron.uhabits.utils.DateUtils;
@ -117,8 +117,7 @@ public class TickmateDBImporter extends AbstractImporter
Habit habit = new Habit(); Habit habit = new Habit();
habit.setName(name); habit.setName(name);
habit.setDescription(description); habit.setDescription(description);
habit.setFreqNum(1); habit.setFrequency(Frequency.DAILY);
habit.setFreqDen(1);
habitList.add(habit); habitList.add(habit);
createCheckmarks(db, habit, id); createCheckmarks(db, habit, id);

@ -30,7 +30,7 @@ import org.apache.commons.lang3.builder.*;
* <p> * <p>
* Checkmarks are computed automatically from the list of repetitions. * Checkmarks are computed automatically from the list of repetitions.
*/ */
public class Checkmark public final class Checkmark
{ {
/** /**
* Indicates that there was a repetition at the timestamp. * Indicates that there was a repetition at the timestamp.

@ -189,7 +189,9 @@ public abstract class CheckmarkList
if (from > to) return; if (from > to) return;
long fromExtended = from - (long) (habit.getFreqDen()) * day; Frequency freq = habit.getFrequency();
long fromExtended = from - (long) (freq.getDenominator()) * day;
List<Repetition> reps = List<Repetition> reps =
habit.getRepetitions().getByInterval(fromExtended, to); habit.getRepetitions().getByInterval(fromExtended, to);
@ -207,10 +209,10 @@ public abstract class CheckmarkList
{ {
int counter = 0; int counter = 0;
for (int j = 0; j < habit.getFreqDen(); j++) for (int j = 0; j < freq.getDenominator(); j++)
if (checks[i + j] == 2) counter++; if (checks[i + j] == 2) counter++;
if (counter >= habit.getFreqNum()) if (counter >= freq.getNumerator())
if (checks[i] != Checkmark.CHECKED_EXPLICITLY) if (checks[i] != Checkmark.CHECKED_EXPLICITLY)
checks[i] = Checkmark.CHECKED_IMPLICITLY; checks[i] = Checkmark.CHECKED_IMPLICITLY;
} }

@ -0,0 +1,97 @@
/*
* Copyright (C) 2016 Á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.models;
import org.apache.commons.lang3.builder.*;
/**
* Represents how often is the habit repeated.
*/
public class Frequency
{
public static final Frequency DAILY = new Frequency(1, 1);
public static final Frequency FIVE_TIMES_PER_WEEK = new Frequency(5, 7);
public static final Frequency THREE_TIMES_PER_WEEK = new Frequency(3, 7);
public static final Frequency TWO_TIMES_PER_WEEK = new Frequency(2, 7);
public static final Frequency WEEKLY = new Frequency(1, 7);
private final int numerator;
private final int denominator;
public Frequency(int numerator, int denominator)
{
if (numerator == denominator) numerator = denominator = 1;
this.numerator = numerator;
this.denominator = denominator;
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Frequency frequency = (Frequency) o;
return new EqualsBuilder()
.append(numerator, frequency.numerator)
.append(denominator, frequency.denominator)
.isEquals();
}
public int getDenominator()
{
return denominator;
}
public int getNumerator()
{
return numerator;
}
@Override
public int hashCode()
{
return new HashCodeBuilder(17, 37)
.append(numerator)
.append(denominator)
.toHashCode();
}
public double toDouble()
{
return (double) numerator / denominator;
}
@Override
public String toString()
{
return new ToStringBuilder(this)
.append("numerator", numerator)
.append("denominator", denominator)
.toString();
}
}

@ -47,19 +47,13 @@ public class Habit
private String description; private String description;
@NonNull @NonNull
private Integer freqNum; private Frequency frequency;
@NonNull
private Integer freqDen;
@NonNull @NonNull
private Integer color; private Integer color;
@NonNull @NonNull
private Integer highlight; private boolean archived;
@NonNull
private Integer archived;
@NonNull @NonNull
private StreakList streaks; private StreakList streaks;
@ -109,10 +103,8 @@ public class Habit
HabitsApplication.getComponent().inject(this); HabitsApplication.getComponent().inject(this);
this.color = 5; this.color = 5;
this.highlight = 0; this.archived = false;
this.archived = 0; this.frequency = new Frequency(3, 7);
this.freqDen = 7;
this.freqNum = 3;
checkmarks = factory.buildCheckmarkList(this); checkmarks = factory.buildCheckmarkList(this);
streaks = factory.buildStreakList(this); streaks = factory.buildStreakList(this);
@ -139,24 +131,13 @@ public class Habit
{ {
this.name = model.getName(); this.name = model.getName();
this.description = model.getDescription(); this.description = model.getDescription();
this.freqNum = model.getFreqNum();
this.freqDen = model.getFreqDen();
this.color = model.getColor(); this.color = model.getColor();
this.archived = model.isArchived();
this.frequency = model.frequency;
this.reminder = model.reminder; this.reminder = model.reminder;
this.highlight = model.getHighlight();
this.archived = model.getArchived();
observable.notifyListeners(); observable.notifyListeners();
} }
/**
* Flag that indicates whether the habit is archived. Archived habits are
* usually omitted from listings, unless explicitly included.
*/
public Integer getArchived()
{
return archived;
}
/** /**
* List of checkmarks belonging to this habit. * List of checkmarks belonging to this habit.
*/ */
@ -179,21 +160,11 @@ public class Habit
return color; return color;
} }
@NonNull
public Reminder getReminder()
{
if(reminder == null) throw new IllegalStateException();
return reminder;
}
public void setColor(Integer color) public void setColor(Integer color)
{ {
this.color = color; this.color = color;
} }
/**
* Description of the habit
*/
public String getDescription() public String getDescription()
{ {
return description; return description;
@ -204,46 +175,15 @@ public class Habit
this.description = description; this.description = description;
} }
/**
* Frequency denominator. If a habit is performed 3 times in 7 days, this
* field equals 7.
*/
public Integer getFreqDen()
{
return freqDen;
}
public void setFreqDen(Integer freqDen)
{
this.freqDen = freqDen;
}
/**
* Frequency numerator. If a habit is performed 3 times in 7 days, this
* field equals 3.
*/
public Integer getFreqNum()
{
return freqNum;
}
public void setFreqNum(@NonNull Integer freqNum)
{
this.freqNum = freqNum;
}
/**
* Not currently used.
*/
@NonNull @NonNull
public Integer getHighlight() public Frequency getFrequency()
{ {
return highlight; return frequency;
} }
public void setHighlight(@NonNull Integer highlight) public void setFrequency(@NonNull Frequency frequency)
{ {
this.highlight = highlight; this.frequency = frequency;
} }
@Nullable @Nullable
@ -257,9 +197,6 @@ public class Habit
this.id = id; this.id = id;
} }
/**
* Name of the habit
*/
@NonNull @NonNull
public String getName() public String getName()
{ {
@ -277,26 +214,39 @@ public class Habit
} }
/** /**
* List of repetitions belonging to this habit. * Returns the reminder for this habit.
* <p>
* Before calling this method, you should call {@link #hasReminder()} to
* verify that a reminder does exist, otherwise an exception will be
* thrown.
*
* @return the reminder for this habit
* @throws IllegalStateException if habit has no reminder
*/ */
@NonNull
public Reminder getReminder()
{
if (reminder == null) throw new IllegalStateException();
return reminder;
}
public void setReminder(@Nullable Reminder reminder)
{
this.reminder = reminder;
}
@NonNull @NonNull
public RepetitionList getRepetitions() public RepetitionList getRepetitions()
{ {
return repetitions; return repetitions;
} }
/**
* List of scores belonging to this habit.
*/
@NonNull @NonNull
public ScoreList getScores() public ScoreList getScores()
{ {
return scores; return scores;
} }
/**
* List of streaks belonging to this habit.
*/
@NonNull @NonNull
public StreakList getStreaks() public StreakList getStreaks()
{ {
@ -315,7 +265,7 @@ public class Habit
} }
/** /**
* Checks whether the habit has a reminder set. * Returns whether the habit has a reminder.
* *
* @return true if habit has reminder, false otherwise * @return true if habit has reminder, false otherwise
*/ */
@ -324,26 +274,16 @@ public class Habit
return reminder != null; return reminder != null;
} }
/**
* Returns whether the habit is archived or not.
*
* @return true if archived
*/
public boolean isArchived() public boolean isArchived()
{ {
return archived != 0; return archived;
} }
public void setArchived(@NonNull Integer archived) public void setArchived(boolean archived)
{ {
this.archived = archived; this.archived = archived;
} }
public void setReminder(@Nullable Reminder reminder)
{
this.reminder = reminder;
}
@Override @Override
public String toString() public String toString()
{ {
@ -351,10 +291,7 @@ public class Habit
.append("id", id) .append("id", id)
.append("name", name) .append("name", name)
.append("description", description) .append("description", description)
.append("freqNum", freqNum)
.append("freqDen", freqDen)
.append("color", color) .append("color", color)
.append("highlight", highlight)
.append("archived", archived) .append("archived", archived)
.toString(); .toString();
} }

@ -208,12 +208,14 @@ public abstract class HabitList
for (Habit habit : getAll(true)) for (Habit habit : getAll(true))
{ {
Frequency freq = habit.getFrequency();
String[] cols = { String[] cols = {
String.format("%03d", indexOf(habit) + 1), String.format("%03d", indexOf(habit) + 1),
habit.getName(), habit.getName(),
habit.getDescription(), habit.getDescription(),
Integer.toString(habit.getFreqNum()), Integer.toString(freq.getNumerator()),
Integer.toString(habit.getFreqDen()), Integer.toString(freq.getDenominator()),
ColorUtils.CSV_PALETTE[habit.getColor()] ColorUtils.CSV_PALETTE[habit.getColor()]
}; };

@ -27,7 +27,7 @@ import java.util.*;
*/ */
public class ModelObservable public class ModelObservable
{ {
List<Listener> listeners; private List<Listener> listeners;
/** /**
* Creates a new ModelObservable with no listeners. * Creates a new ModelObservable with no listeners.
@ -62,7 +62,7 @@ public class ModelObservable
* Removes the given listener. * Removes the given listener.
* <p> * <p>
* The listener will no longer be notified when the model changes. If the * The listener will no longer be notified when the model changes. If the
* given listener is not subscrined to this observable, does nothing. * given listener is not subscribed to this observable, does nothing.
* *
* @param l the listener to be removed * @param l the listener to be removed
*/ */
@ -77,6 +77,10 @@ public class ModelObservable
*/ */
public interface Listener public interface Listener
{ {
/**
* Called whenever the model associated to this observable has been
* modified.
*/
void onModelChange(); void onModelChange();
} }
} }

@ -19,7 +19,7 @@
package org.isoron.uhabits.models; package org.isoron.uhabits.models;
public class Reminder public final class Reminder
{ {
private final int hour; private final int hour;

@ -19,18 +19,14 @@
package org.isoron.uhabits.models; package org.isoron.uhabits.models;
import android.support.annotation.*;
import org.apache.commons.lang3.builder.*; import org.apache.commons.lang3.builder.*;
/** /**
* Represents a record that the user has performed a certain habit at a certain * Represents a record that the user has performed a certain habit at a certain
* date. * date.
*/ */
public class Repetition public final class Repetition
{ {
@NonNull
private final Habit habit;
private final long timestamp; private final long timestamp;
@ -40,20 +36,13 @@ public class Repetition
* The timestamp corresponds to the days this repetition occurred. Time of * The timestamp corresponds to the days this repetition occurred. Time of
* day must be midnight (UTC). * day must be midnight (UTC).
* *
* @param habit the habit to which this repetition belongs.
* @param timestamp the time this repetition occurred. * @param timestamp the time this repetition occurred.
*/ */
public Repetition(Habit habit, long timestamp) public Repetition(long timestamp)
{ {
this.habit = habit;
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public Habit getHabit()
{
return habit;
}
public long getTimestamp() public long getTimestamp()
{ {
return timestamp; return timestamp;

@ -21,7 +21,6 @@ package org.isoron.uhabits.models;
import android.support.annotation.*; import android.support.annotation.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import java.util.*; import java.util.*;
@ -183,7 +182,7 @@ public abstract class RepetitionList
if (rep != null) remove(rep); if (rep != null) remove(rep);
else else
{ {
rep = new Repetition(habit, timestamp); rep = new Repetition(timestamp);
add(rep); add(rep);
} }

@ -24,18 +24,13 @@ import org.apache.commons.lang3.builder.*;
/** /**
* Represents how strong a habit is at a certain date. * Represents how strong a habit is at a certain date.
*/ */
public class Score public final class Score
{ {
/** /**
* Maximum score value attainable by any habit. * Maximum score value attainable by any habit.
*/ */
public static final int MAX_VALUE = 19259478; public static final int MAX_VALUE = 19259478;
/**
* Habit to which this score belongs to.
*/
private Habit habit;
/** /**
* Timestamp of the day to which this score applies. Time of day should be * Timestamp of the day to which this score applies. Time of day should be
* midnight (UTC). * midnight (UTC).
@ -47,9 +42,8 @@ public class Score
*/ */
private final Integer value; private final Integer value;
public Score(Habit habit, Long timestamp, Integer value) public Score(Long timestamp, Integer value)
{ {
this.habit = habit;
this.timestamp = timestamp; this.timestamp = timestamp;
this.value = value; this.value = value;
} }
@ -91,11 +85,6 @@ public class Score
return Long.signum(this.getTimestamp() - other.getTimestamp()); return Long.signum(this.getTimestamp() - other.getTimestamp());
} }
public Habit getHabit()
{
return habit;
}
public Long getTimestamp() public Long getTimestamp()
{ {
return timestamp; return timestamp;

@ -157,7 +157,7 @@ public abstract class ScoreList implements Iterable<Score>
protected void compute(long from, long to) protected void compute(long from, long to)
{ {
final long day = DateUtils.millisecondsInOneDay; final long day = DateUtils.millisecondsInOneDay;
final double freq = ((double) habit.getFreqNum()) / habit.getFreqDen(); final double freq = habit.getFrequency().toDouble();
int newestValue = 0; int newestValue = 0;
long newestTimestamp = 0; long newestTimestamp = 0;
@ -181,7 +181,7 @@ public abstract class ScoreList implements Iterable<Score>
{ {
int value = checkmarkValues[checkmarkValues.length - i - 1]; int value = checkmarkValues[checkmarkValues.length - i - 1];
lastScore = Score.compute(freq, lastScore, value); lastScore = Score.compute(freq, lastScore, value);
scores.add(new Score(habit, beginning + day * i, lastScore)); scores.add(new Score(beginning + day * i, lastScore));
} }
add(scores); add(scores);
@ -241,7 +241,7 @@ public abstract class ScoreList implements Iterable<Score>
for (Long v : groupValues) meanValue += v; for (Long v : groupValues) meanValue += v;
meanValue /= groupValues.size(); meanValue /= groupValues.size();
scores.add(new Score(habit, timestamp, (int) meanValue)); scores.add(new Score(timestamp, (int) meanValue));
} }
return scores; return scores;

@ -22,17 +22,14 @@ package org.isoron.uhabits.models;
import org.apache.commons.lang3.builder.*; import org.apache.commons.lang3.builder.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
public class Streak public final class Streak
{ {
private Habit habit; private final long start;
private long start; private final long end;
private long end; public Streak(long start, long end)
public Streak(Habit habit, long start, long end)
{ {
this.habit = habit;
this.start = start; this.start = start;
this.end = end; this.end = end;
} }
@ -55,11 +52,6 @@ public class Streak
return end; return end;
} }
public Habit getHabit()
{
return habit;
}
public long getLength() public long getLength()
{ {
return (end - start) / DateUtils.millisecondsInOneDay + 1; return (end - start) / DateUtils.millisecondsInOneDay + 1;

@ -97,7 +97,7 @@ public abstract class StreakList
{ {
long start = transitions.get(i); long start = transitions.get(i);
long end = transitions.get(i + 1); long end = transitions.get(i + 1);
streaks.add(new Streak(habit, start, end)); streaks.add(new Streak(start, end));
} }
return streaks; return streaks;

@ -57,8 +57,11 @@ public class SQLiteRepetitionList extends RepetitionList
@Override @Override
public void add(Repetition rep) public void add(Repetition rep)
{ {
check(habit.getId());
RepetitionRecord record = new RepetitionRecord(); RepetitionRecord record = new RepetitionRecord();
record.copyFrom(rep); record.copyFrom(rep);
record.habit = habitRecord;
record.save(); record.save();
observable.notifyListeners(); observable.notifyListeners();
} }

@ -20,12 +20,14 @@
package org.isoron.uhabits.models.sqlite; package org.isoron.uhabits.models.sqlite;
import android.support.annotation.*; import android.support.annotation.*;
import android.support.annotation.Nullable;
import com.activeandroid.query.*; import com.activeandroid.query.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.records.*; import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import org.jetbrains.annotations.*;
import java.util.*; import java.util.*;
@ -34,21 +36,29 @@ import java.util.*;
*/ */
public class SQLiteStreakList extends StreakList public class SQLiteStreakList extends StreakList
{ {
private HabitRecord habitRecord;
@NonNull
private final SQLiteUtils<StreakRecord> sqlite;
public SQLiteStreakList(Habit habit) public SQLiteStreakList(Habit habit)
{ {
super(habit); super(habit);
sqlite = new SQLiteUtils<>(StreakRecord.class);
} }
@Override @Override
public List<Streak> getAll() public List<Streak> getAll()
{ {
check(habit.getId());
rebuild(); rebuild();
List<StreakRecord> records = new Select()
.from(StreakRecord.class)
.where("habit = ?", habit.getId())
.orderBy("end desc")
.execute();
String query = StreakRecord.SELECT + "where habit = ? " +
"order by end desc";
String params[] = { Long.toString(habit.getId())};
List<StreakRecord> records = sqlite.query(query, params);
return recordsToStreaks(records); return recordsToStreaks(records);
} }
@ -75,11 +85,14 @@ public class SQLiteStreakList extends StreakList
@Override @Override
protected void add(@NonNull List<Streak> streaks) protected void add(@NonNull List<Streak> streaks)
{ {
check(habit.getId());
DatabaseUtils.executeAsTransaction(() -> { DatabaseUtils.executeAsTransaction(() -> {
for (Streak streak : streaks) for (Streak streak : streaks)
{ {
StreakRecord record = new StreakRecord(); StreakRecord record = new StreakRecord();
record.copyFrom(streak); record.copyFrom(streak);
record.habit = habitRecord;
record.save(); record.save();
} }
}); });
@ -95,12 +108,15 @@ public class SQLiteStreakList extends StreakList
@Nullable @Nullable
private StreakRecord getNewestRecord() private StreakRecord getNewestRecord()
{ {
return new Select() check(habit.getId());
.from(StreakRecord.class) String query = StreakRecord.SELECT + "where habit = ? " +
.where("habit = ?", habit.getId()) "order by end desc " +
.orderBy("end desc") "limit 1 ";
.limit(1) String params[] = { habit.getId().toString() };
.executeSingle(); StreakRecord record = sqlite.querySingle(query, params);
if (record != null) record.habit = habitRecord;
return record;
} }
@NonNull @NonNull
@ -109,8 +125,22 @@ public class SQLiteStreakList extends StreakList
LinkedList<Streak> streaks = new LinkedList<>(); LinkedList<Streak> streaks = new LinkedList<>();
for (StreakRecord record : records) for (StreakRecord record : records)
{
record.habit = habitRecord;
streaks.add(record.toStreak()); streaks.add(record.toStreak());
}
return streaks; return streaks;
} }
@Contract("null -> fail")
private void check(Long id)
{
if (id == null) throw new RuntimeException("habit is not saved");
if(habitRecord != null) return;
habitRecord = HabitRecord.get(id);
if (habitRecord == null) throw new RuntimeException("habit not found");
}
} }

@ -142,9 +142,13 @@ public class HabitRecord extends Model implements SQLiteRecord
{ {
this.name = model.getName(); this.name = model.getName();
this.description = model.getDescription(); this.description = model.getDescription();
this.freqNum = model.getFreqNum(); this.highlight = 0;
this.freqDen = model.getFreqDen();
this.color = model.getColor(); this.color = model.getColor();
this.archived = model.isArchived() ? 1 : 0;
Frequency freq = model.getFrequency();
this.freqNum = freq.getNumerator();
this.freqDen = freq.getDenominator();
if(model.hasReminder()) if(model.hasReminder())
{ {
Reminder reminder = model.getReminder(); Reminder reminder = model.getReminder();
@ -152,8 +156,6 @@ public class HabitRecord extends Model implements SQLiteRecord
this.reminderMin = reminder.getMinute(); this.reminderMin = reminder.getMinute();
this.reminderDays = reminder.getDays(); this.reminderDays = reminder.getDays();
} }
this.highlight = model.getHighlight();
this.archived = model.getArchived();
} }
@Override @Override
@ -177,11 +179,9 @@ public class HabitRecord extends Model implements SQLiteRecord
{ {
habit.setName(this.name); habit.setName(this.name);
habit.setDescription(this.description); habit.setDescription(this.description);
habit.setFreqNum(this.freqNum); habit.setFrequency(new Frequency(this.freqNum, this.freqDen));
habit.setFreqDen(this.freqDen);
habit.setColor(this.color); habit.setColor(this.color);
habit.setHighlight(this.highlight); habit.setArchived(this.archived != 0);
habit.setArchived(this.archived);
habit.setId(this.getId()); habit.setId(this.getId());
if (reminderHour != null && reminderMin != null) if (reminderHour != null && reminderMin != null)
@ -204,8 +204,6 @@ public class HabitRecord extends Model implements SQLiteRecord
private void setId(Long id) private void setId(Long id)
{ {
// HACK: The id field is declared private by ActiveAndroid and
// there are no setters. (WTF?)
try try
{ {
Field f = (Model.class).getDeclaredField("mId"); Field f = (Model.class).getDeclaredField("mId");

@ -46,7 +46,6 @@ public class RepetitionRecord extends Model implements SQLiteRecord
public void copyFrom(Repetition repetition) public void copyFrom(Repetition repetition)
{ {
habit = HabitRecord.get(repetition.getHabit().getId());
timestamp = repetition.getTimestamp(); timestamp = repetition.getTimestamp();
} }
@ -60,6 +59,6 @@ public class RepetitionRecord extends Model implements SQLiteRecord
{ {
SQLiteHabitList habitList = SQLiteHabitList.getInstance(); SQLiteHabitList habitList = SQLiteHabitList.getInstance();
Habit h = habitList.getById(habit.getId()); Habit h = habitList.getById(habit.getId());
return new Repetition(h, timestamp); return new Repetition(timestamp);
} }
} }

@ -65,6 +65,6 @@ public class ScoreRecord extends Model implements SQLiteRecord
{ {
SQLiteHabitList habitList = SQLiteHabitList.getInstance(); SQLiteHabitList habitList = SQLiteHabitList.getInstance();
Habit h = habitList.getById(habit.getId()); Habit h = habitList.getById(habit.getId());
return new Score(h, timestamp, score); return new Score(timestamp, score);
} }
} }

@ -19,18 +19,24 @@
package org.isoron.uhabits.models.sqlite.records; package org.isoron.uhabits.models.sqlite.records;
import android.database.*;
import com.activeandroid.*; import com.activeandroid.*;
import com.activeandroid.annotation.*; import com.activeandroid.annotation.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.*; import org.isoron.uhabits.models.sqlite.*;
import java.lang.reflect.*;
/** /**
* The SQLite database record corresponding to a Streak. * The SQLite database record corresponding to a Streak.
*/ */
@Table(name = "Streak") @Table(name = "Streak")
public class StreakRecord extends Model public class StreakRecord extends Model implements SQLiteRecord
{ {
public static final String SELECT = "select id, start, end, length from Streak ";
@Column(name = "habit") @Column(name = "habit")
public HabitRecord habit; public HabitRecord habit;
@ -50,16 +56,38 @@ public class StreakRecord extends Model
public void copyFrom(Streak streak) public void copyFrom(Streak streak)
{ {
habit = HabitRecord.get(streak.getHabit().getId());
start = streak.getStart(); start = streak.getStart();
end = streak.getEnd(); end = streak.getEnd();
length = streak.getLength(); length = streak.getLength();
} }
@Override
public void copyFrom(Cursor c)
{
setId(c.getLong(0));
start = c.getLong(1);
end = c.getLong(2);
length = c.getLong(3);
}
private void setId(long id)
{
try
{
Field f = (Model.class).getDeclaredField("mId");
f.setAccessible(true);
f.set(this, id);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
public Streak toStreak() public Streak toStreak()
{ {
SQLiteHabitList habitList = SQLiteHabitList.getInstance(); SQLiteHabitList habitList = SQLiteHabitList.getInstance();
Habit h = habitList.getById(habit.getId()); Habit h = habitList.getById(habit.getId());
return new Streak(h, start, end); return new Streak(start, end);
} }
} }

@ -83,8 +83,7 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment
if (position < 0 || position > 4) throw new IllegalArgumentException(); if (position < 0 || position > 4) throw new IllegalArgumentException();
int freqNums[] = { 1, 1, 2, 5, 3 }; int freqNums[] = { 1, 1, 2, 5, 3 };
int freqDens[] = { 1, 7, 7, 7, 7 }; int freqDens[] = { 1, 7, 7, 7, 7 };
modifiedHabit.setFreqNum(freqNums[position]); modifiedHabit.setFrequency(new Frequency(freqNums[position], freqDens[position]));
modifiedHabit.setFreqDen(freqDens[position]);
helper.populateFrequencyFields(modifiedHabit); helper.populateFrequencyFields(modifiedHabit);
} }

@ -84,8 +84,12 @@ public class BaseDialogHelper
habit.setDescription(tvDescription.getText().toString().trim()); habit.setDescription(tvDescription.getText().toString().trim());
String freqNum = tvFreqNum.getText().toString(); String freqNum = tvFreqNum.getText().toString();
String freqDen = tvFreqDen.getText().toString(); String freqDen = tvFreqDen.getText().toString();
if (!freqNum.isEmpty()) habit.setFreqNum(Integer.parseInt(freqNum)); if (!freqNum.isEmpty() && !freqDen.isEmpty())
if (!freqDen.isEmpty()) habit.setFreqDen(Integer.parseInt(freqDen)); {
int numerator = Integer.parseInt(freqNum);
int denominator = Integer.parseInt(freqDen);
habit.setFrequency(new Frequency(numerator, denominator));
}
} }
void populateColor(int paletteColor) void populateColor(int paletteColor)
@ -99,16 +103,18 @@ public class BaseDialogHelper
{ {
int quickSelectPosition = -1; int quickSelectPosition = -1;
if (habit.getFreqNum().equals(habit.getFreqDen())) Frequency freq = habit.getFrequency();
if (freq.equals(Frequency.DAILY))
quickSelectPosition = 0; quickSelectPosition = 0;
else if (habit.getFreqNum() == 1 && habit.getFreqDen() == 7) else if (freq.equals(Frequency.WEEKLY))
quickSelectPosition = 1; quickSelectPosition = 1;
else if (habit.getFreqNum() == 2 && habit.getFreqDen() == 7) else if (freq.equals(Frequency.TWO_TIMES_PER_WEEK))
quickSelectPosition = 2; quickSelectPosition = 2;
else if (habit.getFreqNum() == 5 && habit.getFreqDen() == 7) else if (freq.equals(Frequency.FIVE_TIMES_PER_WEEK))
quickSelectPosition = 3; quickSelectPosition = 3;
if (quickSelectPosition >= 0) if (quickSelectPosition >= 0)
@ -116,8 +122,8 @@ public class BaseDialogHelper
else showCustomFrequency(); else showCustomFrequency();
tvFreqNum.setText(habit.getFreqNum().toString()); tvFreqNum.setText(Integer.toString(freq.getNumerator()));
tvFreqDen.setText(habit.getFreqDen().toString()); tvFreqDen.setText(Integer.toString(freq.getDenominator()));
} }
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@ -138,8 +144,7 @@ public class BaseDialogHelper
tvReminderTime.setText(time); tvReminderTime.setText(time);
llReminderDays.setVisibility(View.VISIBLE); llReminderDays.setVisibility(View.VISIBLE);
boolean weekdays[] = boolean weekdays[] = DateUtils.unpackWeekdayList(reminder.getDays());
DateUtils.unpackWeekdayList(reminder.getDays());
tvReminderDays.setText( tvReminderDays.setText(
DateUtils.formatWeekdayList(frag.getContext(), weekdays)); DateUtils.formatWeekdayList(frag.getContext(), weekdays));
} }
@ -169,14 +174,16 @@ public class BaseDialogHelper
valid = false; valid = false;
} }
if (habit.getFreqNum() <= 0) Frequency freq = habit.getFrequency();
if (freq.getNumerator() <= 0)
{ {
tvFreqNum.setError( tvFreqNum.setError(
frag.getString(R.string.validation_number_should_be_positive)); frag.getString(R.string.validation_number_should_be_positive));
valid = false; valid = false;
} }
if (habit.getFreqNum() > habit.getFreqDen()) if (freq.getNumerator() > freq.getDenominator())
{ {
tvFreqNum.setError( tvFreqNum.setError(
frag.getString(R.string.validation_at_most_one_rep_per_day)); frag.getString(R.string.validation_at_most_one_rep_per_day));

@ -19,11 +19,9 @@
package org.isoron.uhabits.ui.habits.edit; package org.isoron.uhabits.ui.habits.edit;
import org.isoron.uhabits.HabitsApplication; import org.isoron.uhabits.*;
import org.isoron.uhabits.R; import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.commands.Command; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.commands.CreateHabitCommand;
import org.isoron.uhabits.models.Habit;
public class CreateHabitDialogFragment extends BaseDialogFragment public class CreateHabitDialogFragment extends BaseDialogFragment
{ {
@ -37,8 +35,7 @@ public class CreateHabitDialogFragment extends BaseDialogFragment
protected void initializeHabits() protected void initializeHabits()
{ {
modifiedHabit = new Habit(); modifiedHabit = new Habit();
modifiedHabit.setFreqNum(1); modifiedHabit.setFrequency(Frequency.DAILY);
modifiedHabit.setFreqDen(1);
modifiedHabit.setColor( modifiedHabit.setColor(
prefs.getDefaultHabitColor(modifiedHabit.getColor())); prefs.getDefaultHabitColor(modifiedHabit.getColor()));
} }

@ -42,8 +42,9 @@ public class ShowHabitHelper
if (fragment.habit == null) return ""; if (fragment.habit == null) return "";
Resources resources = fragment.getResources(); Resources resources = fragment.getResources();
Integer freqNum = fragment.habit.getFreqNum(); Frequency freq = fragment.habit.getFrequency();
Integer freqDen = fragment.habit.getFreqDen(); Integer freqNum = freq.getNumerator();
Integer freqDen = freq.getDenominator();
if (freqNum.equals(freqDen)) if (freqNum.equals(freqDen))
return resources.getString(R.string.every_day); return resources.getString(R.string.every_day);

@ -425,7 +425,7 @@ public class HabitScoreView extends ScrollableDataView
int step = Score.MAX_VALUE / 10; int step = Score.MAX_VALUE / 10;
int current = previous + random.nextInt(step * 2) - step; int current = previous + random.nextInt(step * 2) - step;
current = Math.max(0, Math.min(Score.MAX_VALUE, current)); current = Math.max(0, Math.min(Score.MAX_VALUE, current));
scores.add(new Score(habit, timestamp, current)); scores.add(new Score(timestamp, current));
previous = current; previous = current;
timestamp -= day; timestamp -= day;
} }

@ -43,8 +43,7 @@ public class EditHabitCommandTest extends BaseUnitTest
habit = fixtures.createShortHabit(); habit = fixtures.createShortHabit();
habit.setName("original"); habit.setName("original");
habit.setFreqDen(1); habit.setFrequency(Frequency.DAILY);
habit.setFreqNum(1);
modified = new Habit(); modified = new Habit();
modified.copyFrom(habit); modified.copyFrom(habit);
@ -75,8 +74,7 @@ public class EditHabitCommandTest extends BaseUnitTest
@Test @Test
public void testExecuteUndoRedo_withModifiedInterval() public void testExecuteUndoRedo_withModifiedInterval()
{ {
modified.setFreqNum(1); modified.setFrequency(Frequency.TWO_TIMES_PER_WEEK);
modified.setFreqDen(7);
command = new EditHabitCommand(habit, modified); command = new EditHabitCommand(habit, modified);
int originalScore = habit.getScores().getTodayValue(); int originalScore = habit.getScores().getTodayValue();

@ -39,7 +39,7 @@ public class UnarchiveHabitsCommandTest extends BaseUnitTest
super.setUp(); super.setUp();
habit = fixtures.createShortHabit(); habit = fixtures.createShortHabit();
habit.setArchived(1); habit.setArchived(true);
habitList.update(habit); habitList.update(habit);
command = new UnarchiveHabitsCommand(Collections.singletonList(habit)); command = new UnarchiveHabitsCommand(Collections.singletonList(habit));

@ -40,8 +40,7 @@ public class HabitFixtures
habit.setName("Meditate"); habit.setName("Meditate");
habit.setDescription("Did you meditate this morning?"); habit.setDescription("Did you meditate this morning?");
habit.setColor(3); habit.setColor(3);
habit.setFreqNum(1); habit.setFrequency(Frequency.DAILY);
habit.setFreqDen(1);
habitList.add(habit); habitList.add(habit);
return habit; return habit;
} }
@ -49,8 +48,7 @@ public class HabitFixtures
public Habit createLongHabit() public Habit createLongHabit()
{ {
Habit habit = createEmptyHabit(); Habit habit = createEmptyHabit();
habit.setFreqNum(3); habit.setFrequency(new Frequency(3, 7));
habit.setFreqDen(7);
habit.setColor(4); habit.setColor(4);
long day = DateUtils.millisecondsInOneDay; long day = DateUtils.millisecondsInOneDay;
@ -70,8 +68,7 @@ public class HabitFixtures
Habit habit = new Habit(); Habit habit = new Habit();
habit.setName("Wake up early"); habit.setName("Wake up early");
habit.setDescription("Did you wake up before 6am?"); habit.setDescription("Did you wake up before 6am?");
habit.setFreqNum(2); habit.setFrequency(new Frequency(2, 3));
habit.setFreqDen(3);
habitList.add(habit); habitList.add(habit);
long timestamp = DateUtils.getStartOfToday(); long timestamp = DateUtils.getStartOfToday();

@ -57,10 +57,10 @@ public class HabitListTest extends BaseUnitTest
habit.setReminder(new Reminder(8, 30, DateUtils.ALL_WEEK_DAYS)); habit.setReminder(new Reminder(8, 30, DateUtils.ALL_WEEK_DAYS));
} }
habits.get(0).setArchived(1); habits.get(0).setArchived(true);
habits.get(1).setArchived(1); habits.get(1).setArchived(true);
habits.get(4).setArchived(1); habits.get(4).setArchived(true);
habits.get(7).setArchived(1); habits.get(7).setArchived(true);
} }
@Test @Test
@ -166,15 +166,13 @@ public class HabitListTest extends BaseUnitTest
Habit h1 = new Habit(); Habit h1 = new Habit();
h1.setName("Meditate"); h1.setName("Meditate");
h1.setDescription("Did you meditate this morning?"); h1.setDescription("Did you meditate this morning?");
h1.setFreqNum(1); h1.setFrequency(Frequency.DAILY);
h1.setFreqDen(1);
h1.setColor(3); h1.setColor(3);
Habit h2 = new Habit(); Habit h2 = new Habit();
h2.setName("Wake up early"); h2.setName("Wake up early");
h2.setDescription("Did you wake up before 6am?"); h2.setDescription("Did you wake up before 6am?");
h2.setFreqNum(2); h2.setFrequency(new Frequency(2, 3));
h2.setFreqDen(3);
h2.setColor(5); h2.setColor(5);
list.add(h1); list.add(h1);

@ -27,6 +27,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
public class HabitTest extends BaseUnitTest public class HabitTest extends BaseUnitTest
@ -36,8 +37,7 @@ public class HabitTest extends BaseUnitTest
public void testConstructor_default() public void testConstructor_default()
{ {
Habit habit = new Habit(); Habit habit = new Habit();
assertThat(habit.getArchived(), is(0)); assertFalse(habit.isArchived());
assertThat(habit.getHighlight(), is(0));
assertThat(habit.hasReminder(), is(false)); assertThat(habit.hasReminder(), is(false));
assertThat(habit.getStreaks(), is(not(nullValue()))); assertThat(habit.getStreaks(), is(not(nullValue())));
@ -50,20 +50,16 @@ public class HabitTest extends BaseUnitTest
public void test_copyAttributes() public void test_copyAttributes()
{ {
Habit model = new Habit(); Habit model = new Habit();
model.setArchived(1); model.setArchived(true);
model.setHighlight(1);
model.setColor(0); model.setColor(0);
model.setFreqNum(10); model.setFrequency(new Frequency(10, 20));
model.setFreqDen(20);
model.setReminder(new Reminder(8, 30, 1)); model.setReminder(new Reminder(8, 30, 1));
Habit habit = new Habit(); Habit habit = new Habit();
habit.copyFrom(model); habit.copyFrom(model);
assertThat(habit.getArchived(), is(model.getArchived())); assertThat(habit.isArchived(), is(model.isArchived()));
assertThat(habit.getHighlight(), is(model.getHighlight()));
assertThat(habit.getColor(), is(model.getColor())); assertThat(habit.getColor(), is(model.getColor()));
assertThat(habit.getFreqNum(), is(model.getFreqNum())); assertThat(habit.getFrequency(), equalTo(model.getFrequency()));
assertThat(habit.getFreqDen(), is(model.getFreqDen()));
assertThat(habit.getReminder(), equalTo(model.getReminder())); assertThat(habit.getReminder(), equalTo(model.getReminder()));
} }

@ -147,8 +147,7 @@ public class ScoreListTest extends BaseUnitTest
toggleRepetitions(0, 2); toggleRepetitions(0, 2);
assertThat(habit.getScores().getTodayValue(), equalTo(1948077)); assertThat(habit.getScores().getTodayValue(), equalTo(1948077));
habit.setFreqNum(1); habit.setFrequency(new Frequency(1, 2));
habit.setFreqDen(2);
habit.getScores().invalidateNewerThan(0); habit.getScores().invalidateNewerThan(0);
assertThat(habit.getScores().getTodayValue(), equalTo(1974654)); assertThat(habit.getScores().getTodayValue(), equalTo(1974654));

@ -46,8 +46,7 @@ public class StreakListTest extends BaseUnitTest
{ {
super.setUp(); super.setUp();
habit = fixtures.createLongHabit(); habit = fixtures.createLongHabit();
habit.setFreqDen(1); habit.setFrequency(Frequency.DAILY);
habit.setFreqNum(1);
streaks = habit.getStreaks(); streaks = habit.getStreaks();
streaks.rebuild(); streaks.rebuild();

Loading…
Cancel
Save