From ec4a381d7076935e594fd618dd0eca5d04dbc722 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Fri, 17 Jun 2016 06:01:59 -0400 Subject: [PATCH] Refactor Habit class --- .../org/isoron/uhabits/HabitFixtures.java | 12 +- .../org/isoron/uhabits/io/ImportTest.java | 121 ++++++++-------- .../models/sqlite/SQLiteHabitListTest.java | 2 +- .../sqlite/SQLiteRepetitionListTest.java | 4 +- .../models/sqlite/SQLiteScoreListTest.java | 6 +- .../commands/ArchiveHabitsCommand.java | 4 +- .../uhabits/commands/EditHabitCommand.java | 18 ++- .../commands/UnarchiveHabitsCommand.java | 4 +- .../uhabits/io/HabitBullCSVImporter.java | 5 +- .../isoron/uhabits/io/RewireDBImporter.java | 17 ++- .../isoron/uhabits/io/TickmateDBImporter.java | 5 +- .../org/isoron/uhabits/models/Checkmark.java | 2 +- .../isoron/uhabits/models/CheckmarkList.java | 8 +- .../org/isoron/uhabits/models/Frequency.java | 97 +++++++++++++ .../java/org/isoron/uhabits/models/Habit.java | 129 +++++------------- .../org/isoron/uhabits/models/HabitList.java | 6 +- .../uhabits/models/ModelObservable.java | 8 +- .../org/isoron/uhabits/models/Reminder.java | 2 +- .../org/isoron/uhabits/models/Repetition.java | 15 +- .../isoron/uhabits/models/RepetitionList.java | 3 +- .../java/org/isoron/uhabits/models/Score.java | 15 +- .../org/isoron/uhabits/models/ScoreList.java | 6 +- .../org/isoron/uhabits/models/Streak.java | 16 +-- .../org/isoron/uhabits/models/StreakList.java | 2 +- .../models/sqlite/SQLiteRepetitionList.java | 3 + .../models/sqlite/SQLiteStreakList.java | 52 +++++-- .../models/sqlite/records/HabitRecord.java | 18 ++- .../sqlite/records/RepetitionRecord.java | 3 +- .../models/sqlite/records/ScoreRecord.java | 2 +- .../models/sqlite/records/StreakRecord.java | 34 ++++- .../ui/habits/edit/BaseDialogFragment.java | 3 +- .../ui/habits/edit/BaseDialogHelper.java | 31 +++-- .../edit/CreateHabitDialogFragment.java | 11 +- .../ui/habits/show/ShowHabitHelper.java | 5 +- .../ui/habits/show/views/HabitScoreView.java | 2 +- .../commands/EditHabitCommandTest.java | 6 +- .../commands/UnarchiveHabitsCommandTest.java | 2 +- .../isoron/uhabits/models/HabitFixtures.java | 9 +- .../isoron/uhabits/models/HabitListTest.java | 14 +- .../org/isoron/uhabits/models/HabitTest.java | 16 +-- .../isoron/uhabits/models/ScoreListTest.java | 3 +- .../isoron/uhabits/models/StreakListTest.java | 3 +- 42 files changed, 390 insertions(+), 334 deletions(-) create mode 100644 app/src/main/java/org/isoron/uhabits/models/Frequency.java diff --git a/app/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java b/app/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java index 7547691f3..6cf077513 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java +++ b/app/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java @@ -19,8 +19,7 @@ package org.isoron.uhabits; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.HabitList; +import org.isoron.uhabits.models.*; import org.isoron.uhabits.utils.DateUtils; public class HabitFixtures @@ -42,8 +41,7 @@ public class HabitFixtures habit.setName("Meditate"); habit.setDescription("Did you meditate this morning?"); habit.setColor(3); - habit.setFreqNum(1); - habit.setFreqDen(1); + habit.setFrequency(Frequency.DAILY); habitList.add(habit); return habit; } @@ -51,8 +49,7 @@ public class HabitFixtures public Habit createLongHabit() { Habit habit = createEmptyHabit(); - habit.setFreqNum(3); - habit.setFreqDen(7); + habit.setFrequency(new Frequency(3, 7)); habit.setColor(4); long day = DateUtils.millisecondsInOneDay; @@ -72,8 +69,7 @@ public class HabitFixtures Habit habit = new Habit(); habit.setName("Wake up early"); habit.setDescription("Did you wake up before 6am?"); - habit.setFreqNum(2); - habit.setFreqDen(3); + habit.setFrequency(new Frequency(2, 3)); habitList.add(habit); long timestamp = DateUtils.getStartOfToday(); diff --git a/app/src/androidTest/java/org/isoron/uhabits/io/ImportTest.java b/app/src/androidTest/java/org/isoron/uhabits/io/ImportTest.java index fbce70552..e19b035ff 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/io/ImportTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/io/ImportTest.java @@ -41,6 +41,7 @@ import static org.junit.Assert.*; public class ImportTest extends BaseAndroidTest { private File baseDir; + private Context context; @Before @@ -52,49 +53,40 @@ public class ImportTest extends BaseAndroidTest fixtures.purgeHabits(habitList); context = InstrumentationRegistry.getInstrumentation().getContext(); baseDir = FileUtils.getFilesDir("Backups"); - 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); + if (baseDir == null) fail("baseDir should not be null"); } - private void importFromFile(String assetFilename) throws IOException + @Test + public void testHabitBullCSV() throws IOException { - File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename)); - copyAssetToFile(assetFilename, file); - assertTrue(file.exists()); - assertTrue(file.canRead()); - - GenericImporter importer = new GenericImporter(); - assertThat(importer.canHandle(file), is(true)); + importFromFile("habitbull.csv"); - importer.importHabitsFromFile(file); - } + List habits = habitList.getAll(true); + assertThat(habits.size(), equalTo(4)); - private boolean containsRepetition(Habit h, int year, int month, int day) - { - GregorianCalendar date = DateUtils.getStartOfTodayCalendar(); - date.set(year, month - 1, day); - return h.getRepetitions().containsTimestamp(date.getTimeInMillis()); + Habit habit = habits.get(0); + 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)); } @Test - public void testTickmateDB() throws IOException + public void testLoopDB() throws IOException { - importFromFile("tickmate.db"); + importFromFile("loop.db"); List habits = habitList.getAll(true); - assertThat(habits.size(), equalTo(3)); + assertThat(habits.size(), equalTo(9)); - Habit h = habits.get(0); - 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)); + Habit habit = habits.get(0); + assertThat(habit.getName(), equalTo("Wake up early")); + assertThat(habit.getFrequency(), equalTo(Frequency.THREE_TIMES_PER_WEEK)); + assertTrue(containsRepetition(habit, 2016, 3, 14)); + assertTrue(containsRepetition(habit, 2016, 3, 16)); + assertFalse(containsRepetition(habit, 2016, 3, 17)); } @Test @@ -107,8 +99,8 @@ public class ImportTest extends BaseAndroidTest Habit habit = habits.get(0); assertThat(habit.getName(), equalTo("Wake up early")); - assertThat(habit.getFreqNum(), equalTo(3)); - assertThat(habit.getFreqDen(), equalTo(7)); + assertThat(habit.getFrequency(), + equalTo(Frequency.THREE_TIMES_PER_WEEK)); assertFalse(habit.hasReminder()); assertFalse(containsRepetition(habit, 2015, 12, 31)); assertTrue(containsRepetition(habit, 2016, 1, 18)); @@ -117,49 +109,58 @@ public class ImportTest extends BaseAndroidTest habit = habits.get(1); assertThat(habit.getName(), equalTo("brush teeth")); - assertThat(habit.getFreqNum(), equalTo(3)); - assertThat(habit.getFreqDen(), equalTo(7)); + assertThat(habit.getFrequency(), + equalTo(Frequency.THREE_TIMES_PER_WEEK)); assertThat(habit.hasReminder(), equalTo(true)); Reminder reminder = habit.getReminder(); assertThat(reminder.getHour(), equalTo(8)); assertThat(reminder.getMinute(), equalTo(0)); - boolean[] reminderDays = {false, true, true, true, true, true, false}; - assertThat(reminder.getDays(), equalTo(DateUtils.packWeekdayList(reminderDays))); + boolean[] reminderDays = { false, true, true, true, true, true, false }; + assertThat(reminder.getDays(), + equalTo(DateUtils.packWeekdayList(reminderDays))); } @Test - public void testHabitBullCSV() throws IOException + public void testTickmateDB() throws IOException { - importFromFile("habitbull.csv"); + importFromFile("tickmate.db"); List habits = habitList.getAll(true); - assertThat(habits.size(), equalTo(4)); + assertThat(habits.size(), equalTo(3)); - Habit habit = habits.get(0); - assertThat(habit.getName(), equalTo("Breed dragons")); - assertThat(habit.getDescription(), equalTo("with love and fire")); - assertThat(habit.getFreqNum(), equalTo(1)); - assertThat(habit.getFreqDen(), equalTo(1)); - assertTrue(containsRepetition(habit, 2016, 3, 18)); - assertTrue(containsRepetition(habit, 2016, 3, 19)); - assertFalse(containsRepetition(habit, 2016, 3, 20)); + Habit h = habits.get(0); + 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)); } - @Test - public void testLoopDB() throws IOException + private boolean containsRepetition(Habit h, int year, int month, int day) { - importFromFile("loop.db"); + GregorianCalendar date = DateUtils.getStartOfTodayCalendar(); + date.set(year, month - 1, day); + return h.getRepetitions().containsTimestamp(date.getTimeInMillis()); + } - List habits = habitList.getAll(true); - assertThat(habits.size(), equalTo(9)); + private void copyAssetToFile(String assetPath, File dst) throws IOException + { + InputStream in = context.getAssets().open(assetPath); + FileUtils.copy(in, dst); + } - Habit habit = habits.get(0); - assertThat(habit.getName(), equalTo("Wake up early")); - assertThat(habit.getFreqNum(), equalTo(3)); - assertThat(habit.getFreqDen(), equalTo(7)); - assertTrue(containsRepetition(habit, 2016, 3, 14)); - assertTrue(containsRepetition(habit, 2016, 3, 16)); - assertFalse(containsRepetition(habit, 2016, 3, 17)); + private void importFromFile(String assetFilename) throws IOException + { + File file = + new File(String.format("%s/%s", baseDir.getPath(), assetFilename)); + copyAssetToFile(assetFilename, file); + assertTrue(file.exists()); + assertTrue(file.canRead()); + + GenericImporter importer = new GenericImporter(); + assertThat(importer.canHandle(file), is(true)); + + importer.importHabitsFromFile(file); } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteHabitListTest.java b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteHabitListTest.java index 6752e483b..cf8693a60 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteHabitListTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteHabitListTest.java @@ -56,7 +56,7 @@ public class SQLiteHabitListTest extends BaseAndroidTest Habit h = new Habit(); h.setName("habit " + i); h.setId((long) i); - if (i % 2 == 0) h.setArchived(1); + if (i % 2 == 0) h.setArchived(true); HabitRecord record = new HabitRecord(); record.copyFrom(h); diff --git a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java index ee2a579d5..992c1e673 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java @@ -67,7 +67,7 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest RepetitionRecord record = getByTimestamp(today + day); assertThat(record, is(nullValue())); - Repetition rep = new Repetition(habit, today + day); + Repetition rep = new Repetition(today + day); habit.getRepetitions().add(rep); record = getByTimestamp(today + day); @@ -91,7 +91,6 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest { Repetition rep = repetitions.getByTimestamp(today); assertThat(rep, is(not(nullValue()))); - assertThat(rep.getHabit(), equalTo(habit)); assertThat(rep.getTimestamp(), equalTo(today)); rep = repetitions.getByTimestamp(today - 2 * day); @@ -103,7 +102,6 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest { Repetition rep = repetitions.getOldest(); assertThat(rep, is(not(nullValue()))); - assertThat(rep.getHabit(), equalTo(habit)); assertThat(rep.getTimestamp(), equalTo(today - 120 * day)); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteScoreListTest.java b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteScoreListTest.java index 8011b147f..1e05d9ecc 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteScoreListTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteScoreListTest.java @@ -92,9 +92,9 @@ public class SQLiteScoreListTest extends BaseAndroidTest new Delete().from(ScoreRecord.class).execute(); List list = new LinkedList<>(); - list.add(new Score(habit, today, 0)); - list.add(new Score(habit, today - day, 0)); - list.add(new Score(habit, today - 2 * day, 0)); + list.add(new Score(today, 0)); + list.add(new Score(today - day, 0)); + list.add(new Score(today - 2 * day, 0)); scores.add(list); diff --git a/app/src/main/java/org/isoron/uhabits/commands/ArchiveHabitsCommand.java b/app/src/main/java/org/isoron/uhabits/commands/ArchiveHabitsCommand.java index 9e3238010..fa2ca827f 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/ArchiveHabitsCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/ArchiveHabitsCommand.java @@ -47,14 +47,14 @@ public class ArchiveHabitsCommand extends Command @Override public void execute() { - for(Habit h : habits) h.setArchived(1); + for(Habit h : habits) h.setArchived(true); habitList.update(habits); } @Override public void undo() { - for(Habit h : habits) h.setArchived(0); + for(Habit h : habits) h.setArchived(false); habitList.update(habits); } diff --git a/app/src/main/java/org/isoron/uhabits/commands/EditHabitCommand.java b/app/src/main/java/org/isoron/uhabits/commands/EditHabitCommand.java index 90884e119..784d909bf 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/EditHabitCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/EditHabitCommand.java @@ -19,12 +19,10 @@ package org.isoron.uhabits.commands; -import org.isoron.uhabits.HabitsApplication; -import org.isoron.uhabits.R; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.HabitList; +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; -import javax.inject.Inject; +import javax.inject.*; /** * Command to modify a habit. @@ -40,7 +38,7 @@ public class EditHabitCommand extends Command private long savedId; - private boolean hasIntervalChanged; + private boolean hasFrequencyChanged; public EditHabitCommand(Habit original, Habit modified) { @@ -53,9 +51,9 @@ public class EditHabitCommand extends Command this.modified.copyFrom(modified); this.original.copyFrom(original); - hasIntervalChanged = - (!this.original.getFreqDen().equals(this.modified.getFreqDen()) || - !this.original.getFreqNum().equals(this.modified.getFreqNum())); + Frequency originalFreq = this.original.getFrequency(); + Frequency modifiedFreq = this.modified.getFrequency(); + hasFrequencyChanged = (!originalFreq.equals(modifiedFreq)); } @Override @@ -95,7 +93,7 @@ public class EditHabitCommand extends Command private void invalidateIfNeeded(Habit habit) { - if (hasIntervalChanged) + if (hasFrequencyChanged) { habit.getCheckmarks().invalidateNewerThan(0); habit.getStreaks().invalidateNewerThan(0); diff --git a/app/src/main/java/org/isoron/uhabits/commands/UnarchiveHabitsCommand.java b/app/src/main/java/org/isoron/uhabits/commands/UnarchiveHabitsCommand.java index 6f06e61eb..5d442717c 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/UnarchiveHabitsCommand.java +++ b/app/src/main/java/org/isoron/uhabits/commands/UnarchiveHabitsCommand.java @@ -47,14 +47,14 @@ public class UnarchiveHabitsCommand extends Command @Override public void execute() { - for(Habit h : habits) h.setArchived(0); + for(Habit h : habits) h.setArchived(false); habitList.update(habits); } @Override public void undo() { - for(Habit h : habits) h.setArchived(1); + for(Habit h : habits) h.setArchived(true); habitList.update(habits); } diff --git a/app/src/main/java/org/isoron/uhabits/io/HabitBullCSVImporter.java b/app/src/main/java/org/isoron/uhabits/io/HabitBullCSVImporter.java index 0645fd00d..9ce0aa005 100644 --- a/app/src/main/java/org/isoron/uhabits/io/HabitBullCSVImporter.java +++ b/app/src/main/java/org/isoron/uhabits/io/HabitBullCSVImporter.java @@ -24,7 +24,7 @@ import android.support.annotation.NonNull; import com.activeandroid.ActiveAndroid; import com.opencsv.CSVReader; -import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.models.*; import org.isoron.uhabits.utils.DateUtils; import java.io.BufferedReader; @@ -94,8 +94,7 @@ public class HabitBullCSVImporter extends AbstractImporter h = new Habit(); h.setName(name); h.setDescription(description); - h.setFreqDen(1); - h.setFreqNum(1); + h.setFrequency(Frequency.DAILY); habitList.add(h); habits.put(name, h); } diff --git a/app/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java b/app/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java index f91895df9..e9d1d4c83 100644 --- a/app/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java +++ b/app/src/main/java/org/isoron/uhabits/io/RewireDBImporter.java @@ -133,25 +133,30 @@ public class RewireDBImporter extends AbstractImporter habit.setDescription(description); int periods[] = { 7, 31, 365 }; + int numerator, denominator; switch (schedule) { case 0: - habit.setFreqNum(activeDays.split(",").length); - habit.setFreqDen(7); + numerator = activeDays.split(",").length; + denominator = 7; break; case 1: - habit.setFreqNum(days); - habit.setFreqDen(periods[periodIndex]); + numerator = days; + denominator = (periods[periodIndex]); break; case 2: - habit.setFreqNum(1); - habit.setFreqDen(repeatingCount); + numerator = 1; + denominator = repeatingCount; break; + + default: + throw new IllegalStateException(); } + habit.setFrequency(new Frequency(numerator, denominator)); habitList.add(habit); createReminder(db, habit, id); diff --git a/app/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java b/app/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java index c47c5580a..3494468ea 100644 --- a/app/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java +++ b/app/src/main/java/org/isoron/uhabits/io/TickmateDBImporter.java @@ -23,7 +23,7 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; 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.DateUtils; @@ -117,8 +117,7 @@ public class TickmateDBImporter extends AbstractImporter Habit habit = new Habit(); habit.setName(name); habit.setDescription(description); - habit.setFreqNum(1); - habit.setFreqDen(1); + habit.setFrequency(Frequency.DAILY); habitList.add(habit); createCheckmarks(db, habit, id); diff --git a/app/src/main/java/org/isoron/uhabits/models/Checkmark.java b/app/src/main/java/org/isoron/uhabits/models/Checkmark.java index ccc61c194..3ae1c4b1e 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Checkmark.java +++ b/app/src/main/java/org/isoron/uhabits/models/Checkmark.java @@ -30,7 +30,7 @@ import org.apache.commons.lang3.builder.*; *

* 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. diff --git a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java index 3f6f26ba2..701bedc64 100644 --- a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java +++ b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java @@ -189,7 +189,9 @@ public abstract class CheckmarkList if (from > to) return; - long fromExtended = from - (long) (habit.getFreqDen()) * day; + Frequency freq = habit.getFrequency(); + + long fromExtended = from - (long) (freq.getDenominator()) * day; List reps = habit.getRepetitions().getByInterval(fromExtended, to); @@ -207,10 +209,10 @@ public abstract class CheckmarkList { 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 (counter >= habit.getFreqNum()) + if (counter >= freq.getNumerator()) if (checks[i] != Checkmark.CHECKED_EXPLICITLY) checks[i] = Checkmark.CHECKED_IMPLICITLY; } diff --git a/app/src/main/java/org/isoron/uhabits/models/Frequency.java b/app/src/main/java/org/isoron/uhabits/models/Frequency.java new file mode 100644 index 000000000..5b893b5a1 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/models/Frequency.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.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(); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/models/Habit.java b/app/src/main/java/org/isoron/uhabits/models/Habit.java index 381fccaa4..bc85f85c2 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Habit.java +++ b/app/src/main/java/org/isoron/uhabits/models/Habit.java @@ -47,19 +47,13 @@ public class Habit private String description; @NonNull - private Integer freqNum; - - @NonNull - private Integer freqDen; + private Frequency frequency; @NonNull private Integer color; @NonNull - private Integer highlight; - - @NonNull - private Integer archived; + private boolean archived; @NonNull private StreakList streaks; @@ -109,10 +103,8 @@ public class Habit HabitsApplication.getComponent().inject(this); this.color = 5; - this.highlight = 0; - this.archived = 0; - this.freqDen = 7; - this.freqNum = 3; + this.archived = false; + this.frequency = new Frequency(3, 7); checkmarks = factory.buildCheckmarkList(this); streaks = factory.buildStreakList(this); @@ -139,24 +131,13 @@ public class Habit { this.name = model.getName(); this.description = model.getDescription(); - this.freqNum = model.getFreqNum(); - this.freqDen = model.getFreqDen(); this.color = model.getColor(); + this.archived = model.isArchived(); + this.frequency = model.frequency; this.reminder = model.reminder; - this.highlight = model.getHighlight(); - this.archived = model.getArchived(); 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. */ @@ -179,21 +160,11 @@ public class Habit return color; } - @NonNull - public Reminder getReminder() - { - if(reminder == null) throw new IllegalStateException(); - return reminder; - } - public void setColor(Integer color) { this.color = color; } - /** - * Description of the habit - */ public String getDescription() { return description; @@ -204,46 +175,15 @@ public class Habit 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 - 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 @@ -257,9 +197,6 @@ public class Habit this.id = id; } - /** - * Name of the habit - */ @NonNull public String getName() { @@ -277,26 +214,39 @@ public class Habit } /** - * List of repetitions belonging to this habit. + * Returns the reminder for this habit. + *

+ * 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 public RepetitionList getRepetitions() { return repetitions; } - /** - * List of scores belonging to this habit. - */ @NonNull public ScoreList getScores() { return scores; } - /** - * List of streaks belonging to this habit. - */ @NonNull 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 */ @@ -324,26 +274,16 @@ public class Habit return reminder != null; } - /** - * Returns whether the habit is archived or not. - * - * @return true if archived - */ public boolean isArchived() { - return archived != 0; + return archived; } - public void setArchived(@NonNull Integer archived) + public void setArchived(boolean archived) { this.archived = archived; } - public void setReminder(@Nullable Reminder reminder) - { - this.reminder = reminder; - } - @Override public String toString() { @@ -351,10 +291,7 @@ public class Habit .append("id", id) .append("name", name) .append("description", description) - .append("freqNum", freqNum) - .append("freqDen", freqDen) .append("color", color) - .append("highlight", highlight) .append("archived", archived) .toString(); } diff --git a/app/src/main/java/org/isoron/uhabits/models/HabitList.java b/app/src/main/java/org/isoron/uhabits/models/HabitList.java index 4b80e7383..4bd211e7e 100644 --- a/app/src/main/java/org/isoron/uhabits/models/HabitList.java +++ b/app/src/main/java/org/isoron/uhabits/models/HabitList.java @@ -208,12 +208,14 @@ public abstract class HabitList for (Habit habit : getAll(true)) { + Frequency freq = habit.getFrequency(); + String[] cols = { String.format("%03d", indexOf(habit) + 1), habit.getName(), habit.getDescription(), - Integer.toString(habit.getFreqNum()), - Integer.toString(habit.getFreqDen()), + Integer.toString(freq.getNumerator()), + Integer.toString(freq.getDenominator()), ColorUtils.CSV_PALETTE[habit.getColor()] }; diff --git a/app/src/main/java/org/isoron/uhabits/models/ModelObservable.java b/app/src/main/java/org/isoron/uhabits/models/ModelObservable.java index 001bf2695..a762b5f8a 100644 --- a/app/src/main/java/org/isoron/uhabits/models/ModelObservable.java +++ b/app/src/main/java/org/isoron/uhabits/models/ModelObservable.java @@ -27,7 +27,7 @@ import java.util.*; */ public class ModelObservable { - List listeners; + private List listeners; /** * Creates a new ModelObservable with no listeners. @@ -62,7 +62,7 @@ public class ModelObservable * Removes the given listener. *

* 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 */ @@ -77,6 +77,10 @@ public class ModelObservable */ public interface Listener { + /** + * Called whenever the model associated to this observable has been + * modified. + */ void onModelChange(); } } diff --git a/app/src/main/java/org/isoron/uhabits/models/Reminder.java b/app/src/main/java/org/isoron/uhabits/models/Reminder.java index e92bef33a..2377113c3 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Reminder.java +++ b/app/src/main/java/org/isoron/uhabits/models/Reminder.java @@ -19,7 +19,7 @@ package org.isoron.uhabits.models; -public class Reminder +public final class Reminder { private final int hour; diff --git a/app/src/main/java/org/isoron/uhabits/models/Repetition.java b/app/src/main/java/org/isoron/uhabits/models/Repetition.java index bcfb8bc65..72e378205 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Repetition.java +++ b/app/src/main/java/org/isoron/uhabits/models/Repetition.java @@ -19,18 +19,14 @@ package org.isoron.uhabits.models; -import android.support.annotation.*; - import org.apache.commons.lang3.builder.*; /** * Represents a record that the user has performed a certain habit at a certain * date. */ -public class Repetition +public final class Repetition { - @NonNull - private final Habit habit; private final long timestamp; @@ -40,20 +36,13 @@ public class Repetition * The timestamp corresponds to the days this repetition occurred. Time of * day must be midnight (UTC). * - * @param habit the habit to which this repetition belongs. * @param timestamp the time this repetition occurred. */ - public Repetition(Habit habit, long timestamp) + public Repetition(long timestamp) { - this.habit = habit; this.timestamp = timestamp; } - public Habit getHabit() - { - return habit; - } - public long getTimestamp() { return timestamp; diff --git a/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java b/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java index d4f38e5e3..23866b939 100644 --- a/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java +++ b/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java @@ -21,7 +21,6 @@ package org.isoron.uhabits.models; import android.support.annotation.*; -import org.isoron.uhabits.models.*; import org.isoron.uhabits.utils.*; import java.util.*; @@ -183,7 +182,7 @@ public abstract class RepetitionList if (rep != null) remove(rep); else { - rep = new Repetition(habit, timestamp); + rep = new Repetition(timestamp); add(rep); } diff --git a/app/src/main/java/org/isoron/uhabits/models/Score.java b/app/src/main/java/org/isoron/uhabits/models/Score.java index e4f67c34c..c9ebdea14 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Score.java +++ b/app/src/main/java/org/isoron/uhabits/models/Score.java @@ -24,18 +24,13 @@ import org.apache.commons.lang3.builder.*; /** * Represents how strong a habit is at a certain date. */ -public class Score +public final class Score { /** * Maximum score value attainable by any habit. */ 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 * midnight (UTC). @@ -47,9 +42,8 @@ public class Score */ 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.value = value; } @@ -91,11 +85,6 @@ public class Score return Long.signum(this.getTimestamp() - other.getTimestamp()); } - public Habit getHabit() - { - return habit; - } - public Long getTimestamp() { return timestamp; diff --git a/app/src/main/java/org/isoron/uhabits/models/ScoreList.java b/app/src/main/java/org/isoron/uhabits/models/ScoreList.java index 983e6c11d..a8d797233 100644 --- a/app/src/main/java/org/isoron/uhabits/models/ScoreList.java +++ b/app/src/main/java/org/isoron/uhabits/models/ScoreList.java @@ -157,7 +157,7 @@ public abstract class ScoreList implements Iterable protected void compute(long from, long to) { final long day = DateUtils.millisecondsInOneDay; - final double freq = ((double) habit.getFreqNum()) / habit.getFreqDen(); + final double freq = habit.getFrequency().toDouble(); int newestValue = 0; long newestTimestamp = 0; @@ -181,7 +181,7 @@ public abstract class ScoreList implements Iterable { int value = checkmarkValues[checkmarkValues.length - i - 1]; 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); @@ -241,7 +241,7 @@ public abstract class ScoreList implements Iterable for (Long v : groupValues) meanValue += v; meanValue /= groupValues.size(); - scores.add(new Score(habit, timestamp, (int) meanValue)); + scores.add(new Score(timestamp, (int) meanValue)); } return scores; diff --git a/app/src/main/java/org/isoron/uhabits/models/Streak.java b/app/src/main/java/org/isoron/uhabits/models/Streak.java index 107e5fe86..5a7a59f3d 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Streak.java +++ b/app/src/main/java/org/isoron/uhabits/models/Streak.java @@ -22,17 +22,14 @@ package org.isoron.uhabits.models; import org.apache.commons.lang3.builder.*; 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(Habit habit, long start, long end) + public Streak(long start, long end) { - this.habit = habit; this.start = start; this.end = end; } @@ -55,11 +52,6 @@ public class Streak return end; } - public Habit getHabit() - { - return habit; - } - public long getLength() { return (end - start) / DateUtils.millisecondsInOneDay + 1; diff --git a/app/src/main/java/org/isoron/uhabits/models/StreakList.java b/app/src/main/java/org/isoron/uhabits/models/StreakList.java index c26b3e284..4ae6a2b55 100644 --- a/app/src/main/java/org/isoron/uhabits/models/StreakList.java +++ b/app/src/main/java/org/isoron/uhabits/models/StreakList.java @@ -97,7 +97,7 @@ public abstract class StreakList { long start = transitions.get(i); long end = transitions.get(i + 1); - streaks.add(new Streak(habit, start, end)); + streaks.add(new Streak(start, end)); } return streaks; diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java index 595caf444..2ce44ceb4 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionList.java @@ -57,8 +57,11 @@ public class SQLiteRepetitionList extends RepetitionList @Override public void add(Repetition rep) { + check(habit.getId()); + RepetitionRecord record = new RepetitionRecord(); record.copyFrom(rep); + record.habit = habitRecord; record.save(); observable.notifyListeners(); } diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteStreakList.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteStreakList.java index 322fd60e8..8cf10b49f 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteStreakList.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteStreakList.java @@ -20,12 +20,14 @@ package org.isoron.uhabits.models.sqlite; import android.support.annotation.*; +import android.support.annotation.Nullable; import com.activeandroid.query.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.sqlite.records.*; import org.isoron.uhabits.utils.*; +import org.jetbrains.annotations.*; import java.util.*; @@ -34,21 +36,29 @@ import java.util.*; */ public class SQLiteStreakList extends StreakList { + private HabitRecord habitRecord; + + @NonNull + private final SQLiteUtils sqlite; + public SQLiteStreakList(Habit habit) { super(habit); + sqlite = new SQLiteUtils<>(StreakRecord.class); } @Override public List getAll() { + check(habit.getId()); rebuild(); - List 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 records = sqlite.query(query, params); return recordsToStreaks(records); } @@ -75,11 +85,14 @@ public class SQLiteStreakList extends StreakList @Override protected void add(@NonNull List streaks) { + check(habit.getId()); + DatabaseUtils.executeAsTransaction(() -> { for (Streak streak : streaks) { StreakRecord record = new StreakRecord(); record.copyFrom(streak); + record.habit = habitRecord; record.save(); } }); @@ -95,12 +108,15 @@ public class SQLiteStreakList extends StreakList @Nullable private StreakRecord getNewestRecord() { - return new Select() - .from(StreakRecord.class) - .where("habit = ?", habit.getId()) - .orderBy("end desc") - .limit(1) - .executeSingle(); + check(habit.getId()); + String query = StreakRecord.SELECT + "where habit = ? " + + "order by end desc " + + "limit 1 "; + String params[] = { habit.getId().toString() }; + StreakRecord record = sqlite.querySingle(query, params); + if (record != null) record.habit = habitRecord; + return record; + } @NonNull @@ -109,8 +125,22 @@ public class SQLiteStreakList extends StreakList LinkedList streaks = new LinkedList<>(); for (StreakRecord record : records) + { + record.habit = habitRecord; streaks.add(record.toStreak()); + } 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"); + } } diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/HabitRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/HabitRecord.java index 738232756..154312264 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/HabitRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/HabitRecord.java @@ -142,9 +142,13 @@ public class HabitRecord extends Model implements SQLiteRecord { this.name = model.getName(); this.description = model.getDescription(); - this.freqNum = model.getFreqNum(); - this.freqDen = model.getFreqDen(); + this.highlight = 0; 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()) { Reminder reminder = model.getReminder(); @@ -152,8 +156,6 @@ public class HabitRecord extends Model implements SQLiteRecord this.reminderMin = reminder.getMinute(); this.reminderDays = reminder.getDays(); } - this.highlight = model.getHighlight(); - this.archived = model.getArchived(); } @Override @@ -177,11 +179,9 @@ public class HabitRecord extends Model implements SQLiteRecord { habit.setName(this.name); habit.setDescription(this.description); - habit.setFreqNum(this.freqNum); - habit.setFreqDen(this.freqDen); + habit.setFrequency(new Frequency(this.freqNum, this.freqDen)); habit.setColor(this.color); - habit.setHighlight(this.highlight); - habit.setArchived(this.archived); + habit.setArchived(this.archived != 0); habit.setId(this.getId()); if (reminderHour != null && reminderMin != null) @@ -204,8 +204,6 @@ public class HabitRecord extends Model implements SQLiteRecord private void setId(Long id) { - // HACK: The id field is declared private by ActiveAndroid and - // there are no setters. (WTF?) try { Field f = (Model.class).getDeclaredField("mId"); diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java index d2a7667ec..eec93777f 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java @@ -46,7 +46,6 @@ public class RepetitionRecord extends Model implements SQLiteRecord public void copyFrom(Repetition repetition) { - habit = HabitRecord.get(repetition.getHabit().getId()); timestamp = repetition.getTimestamp(); } @@ -60,6 +59,6 @@ public class RepetitionRecord extends Model implements SQLiteRecord { SQLiteHabitList habitList = SQLiteHabitList.getInstance(); Habit h = habitList.getById(habit.getId()); - return new Repetition(h, timestamp); + return new Repetition(timestamp); } } diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/ScoreRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/ScoreRecord.java index 24bc65509..09d8602a2 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/ScoreRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/ScoreRecord.java @@ -65,6 +65,6 @@ public class ScoreRecord extends Model implements SQLiteRecord { SQLiteHabitList habitList = SQLiteHabitList.getInstance(); Habit h = habitList.getById(habit.getId()); - return new Score(h, timestamp, score); + return new Score(timestamp, score); } } diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/StreakRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/StreakRecord.java index c1b4b0a56..90b4362f2 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/records/StreakRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/StreakRecord.java @@ -19,18 +19,24 @@ package org.isoron.uhabits.models.sqlite.records; +import android.database.*; + import com.activeandroid.*; import com.activeandroid.annotation.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.sqlite.*; +import java.lang.reflect.*; + /** * The SQLite database record corresponding to a 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") public HabitRecord habit; @@ -50,16 +56,38 @@ public class StreakRecord extends Model public void copyFrom(Streak streak) { - habit = HabitRecord.get(streak.getHabit().getId()); start = streak.getStart(); end = streak.getEnd(); 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() { SQLiteHabitList habitList = SQLiteHabitList.getInstance(); Habit h = habitList.getById(habit.getId()); - return new Streak(h, start, end); + return new Streak(start, end); } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogFragment.java b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogFragment.java index d0ee67b85..8b597694f 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogFragment.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogFragment.java @@ -83,8 +83,7 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment if (position < 0 || position > 4) throw new IllegalArgumentException(); int freqNums[] = { 1, 1, 2, 5, 3 }; int freqDens[] = { 1, 7, 7, 7, 7 }; - modifiedHabit.setFreqNum(freqNums[position]); - modifiedHabit.setFreqDen(freqDens[position]); + modifiedHabit.setFrequency(new Frequency(freqNums[position], freqDens[position])); helper.populateFrequencyFields(modifiedHabit); } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogHelper.java b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogHelper.java index 93790f403..3cad2ae8d 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogHelper.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogHelper.java @@ -84,8 +84,12 @@ public class BaseDialogHelper habit.setDescription(tvDescription.getText().toString().trim()); String freqNum = tvFreqNum.getText().toString(); String freqDen = tvFreqDen.getText().toString(); - if (!freqNum.isEmpty()) habit.setFreqNum(Integer.parseInt(freqNum)); - if (!freqDen.isEmpty()) habit.setFreqDen(Integer.parseInt(freqDen)); + if (!freqNum.isEmpty() && !freqDen.isEmpty()) + { + int numerator = Integer.parseInt(freqNum); + int denominator = Integer.parseInt(freqDen); + habit.setFrequency(new Frequency(numerator, denominator)); + } } void populateColor(int paletteColor) @@ -99,16 +103,18 @@ public class BaseDialogHelper { int quickSelectPosition = -1; - if (habit.getFreqNum().equals(habit.getFreqDen())) + Frequency freq = habit.getFrequency(); + + if (freq.equals(Frequency.DAILY)) quickSelectPosition = 0; - else if (habit.getFreqNum() == 1 && habit.getFreqDen() == 7) + else if (freq.equals(Frequency.WEEKLY)) quickSelectPosition = 1; - else if (habit.getFreqNum() == 2 && habit.getFreqDen() == 7) + else if (freq.equals(Frequency.TWO_TIMES_PER_WEEK)) quickSelectPosition = 2; - else if (habit.getFreqNum() == 5 && habit.getFreqDen() == 7) + else if (freq.equals(Frequency.FIVE_TIMES_PER_WEEK)) quickSelectPosition = 3; if (quickSelectPosition >= 0) @@ -116,8 +122,8 @@ public class BaseDialogHelper else showCustomFrequency(); - tvFreqNum.setText(habit.getFreqNum().toString()); - tvFreqDen.setText(habit.getFreqDen().toString()); + tvFreqNum.setText(Integer.toString(freq.getNumerator())); + tvFreqDen.setText(Integer.toString(freq.getDenominator())); } @SuppressWarnings("ConstantConditions") @@ -138,8 +144,7 @@ public class BaseDialogHelper tvReminderTime.setText(time); llReminderDays.setVisibility(View.VISIBLE); - boolean weekdays[] = - DateUtils.unpackWeekdayList(reminder.getDays()); + boolean weekdays[] = DateUtils.unpackWeekdayList(reminder.getDays()); tvReminderDays.setText( DateUtils.formatWeekdayList(frag.getContext(), weekdays)); } @@ -169,14 +174,16 @@ public class BaseDialogHelper valid = false; } - if (habit.getFreqNum() <= 0) + Frequency freq = habit.getFrequency(); + + if (freq.getNumerator() <= 0) { tvFreqNum.setError( frag.getString(R.string.validation_number_should_be_positive)); valid = false; } - if (habit.getFreqNum() > habit.getFreqDen()) + if (freq.getNumerator() > freq.getDenominator()) { tvFreqNum.setError( frag.getString(R.string.validation_at_most_one_rep_per_day)); diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialogFragment.java b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialogFragment.java index 777c32f7c..28188964d 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialogFragment.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialogFragment.java @@ -19,11 +19,9 @@ package org.isoron.uhabits.ui.habits.edit; -import org.isoron.uhabits.HabitsApplication; -import org.isoron.uhabits.R; -import org.isoron.uhabits.commands.Command; -import org.isoron.uhabits.commands.CreateHabitCommand; -import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.*; +import org.isoron.uhabits.commands.*; +import org.isoron.uhabits.models.*; public class CreateHabitDialogFragment extends BaseDialogFragment { @@ -37,8 +35,7 @@ public class CreateHabitDialogFragment extends BaseDialogFragment protected void initializeHabits() { modifiedHabit = new Habit(); - modifiedHabit.setFreqNum(1); - modifiedHabit.setFreqDen(1); + modifiedHabit.setFrequency(Frequency.DAILY); modifiedHabit.setColor( prefs.getDefaultHabitColor(modifiedHabit.getColor())); } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitHelper.java b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitHelper.java index d6a53096a..8539f5792 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitHelper.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitHelper.java @@ -42,8 +42,9 @@ public class ShowHabitHelper if (fragment.habit == null) return ""; Resources resources = fragment.getResources(); - Integer freqNum = fragment.habit.getFreqNum(); - Integer freqDen = fragment.habit.getFreqDen(); + Frequency freq = fragment.habit.getFrequency(); + Integer freqNum = freq.getNumerator(); + Integer freqDen = freq.getDenominator(); if (freqNum.equals(freqDen)) return resources.getString(R.string.every_day); diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/HabitScoreView.java b/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/HabitScoreView.java index 2d125283a..b162b9786 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/HabitScoreView.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/HabitScoreView.java @@ -425,7 +425,7 @@ public class HabitScoreView extends ScrollableDataView int step = Score.MAX_VALUE / 10; int current = previous + random.nextInt(step * 2) - step; 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; timestamp -= day; } diff --git a/app/src/test/java/org/isoron/uhabits/commands/EditHabitCommandTest.java b/app/src/test/java/org/isoron/uhabits/commands/EditHabitCommandTest.java index 8f7fc1a1c..b4f09036a 100644 --- a/app/src/test/java/org/isoron/uhabits/commands/EditHabitCommandTest.java +++ b/app/src/test/java/org/isoron/uhabits/commands/EditHabitCommandTest.java @@ -43,8 +43,7 @@ public class EditHabitCommandTest extends BaseUnitTest habit = fixtures.createShortHabit(); habit.setName("original"); - habit.setFreqDen(1); - habit.setFreqNum(1); + habit.setFrequency(Frequency.DAILY); modified = new Habit(); modified.copyFrom(habit); @@ -75,8 +74,7 @@ public class EditHabitCommandTest extends BaseUnitTest @Test public void testExecuteUndoRedo_withModifiedInterval() { - modified.setFreqNum(1); - modified.setFreqDen(7); + modified.setFrequency(Frequency.TWO_TIMES_PER_WEEK); command = new EditHabitCommand(habit, modified); int originalScore = habit.getScores().getTodayValue(); diff --git a/app/src/test/java/org/isoron/uhabits/commands/UnarchiveHabitsCommandTest.java b/app/src/test/java/org/isoron/uhabits/commands/UnarchiveHabitsCommandTest.java index 1c952e663..1d5eddd73 100644 --- a/app/src/test/java/org/isoron/uhabits/commands/UnarchiveHabitsCommandTest.java +++ b/app/src/test/java/org/isoron/uhabits/commands/UnarchiveHabitsCommandTest.java @@ -39,7 +39,7 @@ public class UnarchiveHabitsCommandTest extends BaseUnitTest super.setUp(); habit = fixtures.createShortHabit(); - habit.setArchived(1); + habit.setArchived(true); habitList.update(habit); command = new UnarchiveHabitsCommand(Collections.singletonList(habit)); diff --git a/app/src/test/java/org/isoron/uhabits/models/HabitFixtures.java b/app/src/test/java/org/isoron/uhabits/models/HabitFixtures.java index 70f7f1131..0ff459c30 100644 --- a/app/src/test/java/org/isoron/uhabits/models/HabitFixtures.java +++ b/app/src/test/java/org/isoron/uhabits/models/HabitFixtures.java @@ -40,8 +40,7 @@ public class HabitFixtures habit.setName("Meditate"); habit.setDescription("Did you meditate this morning?"); habit.setColor(3); - habit.setFreqNum(1); - habit.setFreqDen(1); + habit.setFrequency(Frequency.DAILY); habitList.add(habit); return habit; } @@ -49,8 +48,7 @@ public class HabitFixtures public Habit createLongHabit() { Habit habit = createEmptyHabit(); - habit.setFreqNum(3); - habit.setFreqDen(7); + habit.setFrequency(new Frequency(3, 7)); habit.setColor(4); long day = DateUtils.millisecondsInOneDay; @@ -70,8 +68,7 @@ public class HabitFixtures Habit habit = new Habit(); habit.setName("Wake up early"); habit.setDescription("Did you wake up before 6am?"); - habit.setFreqNum(2); - habit.setFreqDen(3); + habit.setFrequency(new Frequency(2, 3)); habitList.add(habit); long timestamp = DateUtils.getStartOfToday(); diff --git a/app/src/test/java/org/isoron/uhabits/models/HabitListTest.java b/app/src/test/java/org/isoron/uhabits/models/HabitListTest.java index 7175819a6..8d0e4590c 100644 --- a/app/src/test/java/org/isoron/uhabits/models/HabitListTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/HabitListTest.java @@ -57,10 +57,10 @@ public class HabitListTest extends BaseUnitTest habit.setReminder(new Reminder(8, 30, DateUtils.ALL_WEEK_DAYS)); } - habits.get(0).setArchived(1); - habits.get(1).setArchived(1); - habits.get(4).setArchived(1); - habits.get(7).setArchived(1); + habits.get(0).setArchived(true); + habits.get(1).setArchived(true); + habits.get(4).setArchived(true); + habits.get(7).setArchived(true); } @Test @@ -166,15 +166,13 @@ public class HabitListTest extends BaseUnitTest Habit h1 = new Habit(); h1.setName("Meditate"); h1.setDescription("Did you meditate this morning?"); - h1.setFreqNum(1); - h1.setFreqDen(1); + h1.setFrequency(Frequency.DAILY); h1.setColor(3); Habit h2 = new Habit(); h2.setName("Wake up early"); h2.setDescription("Did you wake up before 6am?"); - h2.setFreqNum(2); - h2.setFreqDen(3); + h2.setFrequency(new Frequency(2, 3)); h2.setColor(5); list.add(h1); diff --git a/app/src/test/java/org/isoron/uhabits/models/HabitTest.java b/app/src/test/java/org/isoron/uhabits/models/HabitTest.java index 5bc1b1f26..5b39641db 100644 --- a/app/src/test/java/org/isoron/uhabits/models/HabitTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/HabitTest.java @@ -27,6 +27,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; public class HabitTest extends BaseUnitTest @@ -36,8 +37,7 @@ public class HabitTest extends BaseUnitTest public void testConstructor_default() { Habit habit = new Habit(); - assertThat(habit.getArchived(), is(0)); - assertThat(habit.getHighlight(), is(0)); + assertFalse(habit.isArchived()); assertThat(habit.hasReminder(), is(false)); assertThat(habit.getStreaks(), is(not(nullValue()))); @@ -50,20 +50,16 @@ public class HabitTest extends BaseUnitTest public void test_copyAttributes() { Habit model = new Habit(); - model.setArchived(1); - model.setHighlight(1); + model.setArchived(true); model.setColor(0); - model.setFreqNum(10); - model.setFreqDen(20); + model.setFrequency(new Frequency(10, 20)); model.setReminder(new Reminder(8, 30, 1)); Habit habit = new Habit(); habit.copyFrom(model); - assertThat(habit.getArchived(), is(model.getArchived())); - assertThat(habit.getHighlight(), is(model.getHighlight())); + assertThat(habit.isArchived(), is(model.isArchived())); assertThat(habit.getColor(), is(model.getColor())); - assertThat(habit.getFreqNum(), is(model.getFreqNum())); - assertThat(habit.getFreqDen(), is(model.getFreqDen())); + assertThat(habit.getFrequency(), equalTo(model.getFrequency())); assertThat(habit.getReminder(), equalTo(model.getReminder())); } diff --git a/app/src/test/java/org/isoron/uhabits/models/ScoreListTest.java b/app/src/test/java/org/isoron/uhabits/models/ScoreListTest.java index 8b1007575..489506e61 100644 --- a/app/src/test/java/org/isoron/uhabits/models/ScoreListTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/ScoreListTest.java @@ -147,8 +147,7 @@ public class ScoreListTest extends BaseUnitTest toggleRepetitions(0, 2); assertThat(habit.getScores().getTodayValue(), equalTo(1948077)); - habit.setFreqNum(1); - habit.setFreqDen(2); + habit.setFrequency(new Frequency(1, 2)); habit.getScores().invalidateNewerThan(0); assertThat(habit.getScores().getTodayValue(), equalTo(1974654)); diff --git a/app/src/test/java/org/isoron/uhabits/models/StreakListTest.java b/app/src/test/java/org/isoron/uhabits/models/StreakListTest.java index a4253d543..6ca9a3b5d 100644 --- a/app/src/test/java/org/isoron/uhabits/models/StreakListTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/StreakListTest.java @@ -46,8 +46,7 @@ public class StreakListTest extends BaseUnitTest { super.setUp(); habit = fixtures.createLongHabit(); - habit.setFreqDen(1); - habit.setFreqNum(1); + habit.setFrequency(Frequency.DAILY); streaks = habit.getStreaks(); streaks.rebuild();