From 9a447742841de02f9ed3b9d4a2c562801e680244 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Sun, 12 Jun 2016 07:55:47 -0400 Subject: [PATCH] Add instrumented unit tests for SQLite lists --- app/build.gradle | 3 +- .../org/isoron/uhabits/BaseAndroidTest.java | 4 +- .../sqlite/SQLiteCheckmarkListTest.java | 117 +++++++++ .../models/sqlite/SQLiteHabitListTest.java | 229 ++++++++++++++++++ .../sqlite/SQLiteRepetitionListTest.java | 145 +++++++++++ .../models/sqlite/SQLiteScoreListTest.java | 126 ++++++++++ .../uhabits/ui/MainActivityActions.java | 2 +- .../java/org/isoron/uhabits/ui/MainTest.java | 2 +- .../commands/ArchiveHabitsCommandTest.java | 3 +- .../commands/ChangeHabitColorCommandTest.java | 3 +- .../unit/commands/CreateHabitCommandTest.java | 3 +- .../commands/DeleteHabitsCommandTest.java | 30 ++- .../unit/commands/EditHabitCommandTest.java | 2 +- .../commands/ToggleRepetitionCommandTest.java | 2 +- .../commands/UnarchiveHabitsCommandTest.java | 2 +- .../unit/io/HabitsCSVExporterTest.java | 6 +- .../isoron/uhabits/unit/io/ImportTest.java | 2 +- .../uhabits/unit/models/ScoreListTest.java | 33 --- .../uhabits/unit/tasks/ExportCSVTaskTest.java | 29 +-- .../unit/views/CheckmarkWidgetViewTest.java | 2 +- .../unit/views/HabitFrequencyViewTest.java | 4 +- .../unit/views/HabitHistoryViewTest.java | 4 +- .../unit/views/HabitScoreViewTest.java | 4 +- .../unit/views/HabitStreakViewTest.java | 4 +- .../org/isoron/uhabits/models/Checkmark.java | 7 +- .../isoron/uhabits/models/CheckmarkList.java | 101 +++++--- .../java/org/isoron/uhabits/models/Habit.java | 27 ++- .../org/isoron/uhabits/models/HabitList.java | 32 +-- .../isoron/uhabits/models/ModelFactory.java | 10 +- .../uhabits/models/ModelObservable.java | 3 +- .../org/isoron/uhabits/models/Repetition.java | 4 +- .../isoron/uhabits/models/RepetitionList.java | 20 +- .../java/org/isoron/uhabits/models/Score.java | 12 +- .../org/isoron/uhabits/models/ScoreList.java | 84 ++++--- .../org/isoron/uhabits/models/Streak.java | 4 +- .../org/isoron/uhabits/models/StreakList.java | 14 +- .../models/memory/MemoryCheckmarkList.java | 48 ++-- .../models/memory/MemoryHabitList.java | 37 +-- .../models/memory/MemoryModelFactory.java | 10 +- .../models/memory/MemoryRepetitionList.java | 12 +- .../models/memory/MemoryScoreList.java | 41 ++-- .../models/memory/MemoryStreakList.java | 23 +- .../models/sqlite/SQLModelFactory.java | 12 +- .../models/sqlite/SQLiteCheckmarkList.java | 145 +++++------ .../models/sqlite/SQLiteHabitList.java | 37 +-- .../models/sqlite/SQLiteRepetitionList.java | 87 +++---- .../models/sqlite/SQLiteScoreList.java | 94 +++---- .../models/sqlite/SQLiteStreakList.java | 57 ++--- .../sqlite/{ => records}/CheckmarkRecord.java | 11 +- .../sqlite/{ => records}/HabitRecord.java | 22 +- .../{ => records}/RepetitionRecord.java | 11 +- .../sqlite/{ => records}/ScoreRecord.java | 11 +- .../sqlite/{ => records}/StreakRecord.java | 11 +- .../ui/habits/show/views/HabitScoreView.java | 4 +- .../isoron/uhabits/utils/DatabaseUtils.java | 30 +-- .../widgets/CheckmarkWidgetProvider.java | 36 +-- .../uhabits/models/CheckmarkListTest.java | 1 + .../isoron/uhabits/models/HabitListTest.java | 27 +-- .../uhabits/models/RepetitionListTest.java | 3 +- .../isoron/uhabits/models/ScoreListTest.java | 2 +- .../isoron/uhabits/models/StreakListTest.java | 11 +- 61 files changed, 1194 insertions(+), 668 deletions(-) create mode 100644 app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkListTest.java create mode 100644 app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteHabitListTest.java create mode 100644 app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java create mode 100644 app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteScoreListTest.java delete mode 100644 app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreListTest.java rename app/src/main/java/org/isoron/uhabits/models/sqlite/{ => records}/CheckmarkRecord.java (87%) rename app/src/main/java/org/isoron/uhabits/models/sqlite/{ => records}/HabitRecord.java (89%) rename app/src/main/java/org/isoron/uhabits/models/sqlite/{ => records}/RepetitionRecord.java (86%) rename app/src/main/java/org/isoron/uhabits/models/sqlite/{ => records}/ScoreRecord.java (87%) rename app/src/main/java/org/isoron/uhabits/models/sqlite/{ => records}/StreakRecord.java (87%) diff --git a/app/build.gradle b/app/build.gradle index 93b68ca48..b7e988bb1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,6 +15,7 @@ android { buildConfigField "String", "databaseFilename", "\"uhabits.db\"" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunnerArgument "size", "medium" } buildTypes { @@ -40,7 +41,7 @@ android { unitTests.all { testLogging { events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} + outputs.upToDateWhen { false } showStandardStreams = true } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java b/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java index 8be833bc0..94278eb01 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java @@ -55,7 +55,7 @@ public class BaseAndroidTest protected AndroidTestComponent androidTestComponent; - protected HabitFixtures habitFixtures; + protected HabitFixtures fixtures; @Before public void setUp() @@ -76,7 +76,7 @@ public class BaseAndroidTest HabitsApplication.setComponent(androidTestComponent); androidTestComponent.inject(this); - habitFixtures = new HabitFixtures(habitList); + fixtures = new HabitFixtures(habitList); } protected void waitForAsyncTasks() diff --git a/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkListTest.java b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkListTest.java new file mode 100644 index 000000000..4ccb87016 --- /dev/null +++ b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkListTest.java @@ -0,0 +1,117 @@ +/* + * 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.sqlite; + +import android.support.test.runner.*; +import android.test.suitebuilder.annotation.*; + +import com.activeandroid.query.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; +import org.isoron.uhabits.utils.*; +import org.junit.*; +import org.junit.runner.*; + +import java.util.*; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +@RunWith(AndroidJUnit4.class) +@MediumTest +public class SQLiteCheckmarkListTest extends BaseAndroidTest +{ + private Habit habit; + + private CheckmarkList checkmarks; + + private long today; + + private long day; + + @Override + public void setUp() + { + super.setUp(); + + habit = fixtures.createLongHabit(); + checkmarks = habit.getCheckmarks(); + checkmarks.getToday(); // compute checkmarks + + today = DateUtils.getStartOfToday(); + day = DateUtils.millisecondsInOneDay; + } + + @Test + public void testAdd() + { + checkmarks.invalidateNewerThan(0); + + List list = new LinkedList<>(); + list.add(new Checkmark(habit, 0, 0)); + list.add(new Checkmark(habit, 1, 1)); + list.add(new Checkmark(habit, 2, 2)); + + checkmarks.add(list); + + List records = getAllRecords(); + assertThat(records.size(), equalTo(3)); + assertThat(records.get(0).timestamp, equalTo(2L)); + } + + @Test + public void testGetByInterval() + { + long from = today - 10 * day; + long to = today - 3 * day; + + List list = checkmarks.getByInterval(from, to); + assertThat(list.size(), equalTo(8)); + + assertThat(list.get(0).getTimestamp(), equalTo(today - 3 * day)); + assertThat(list.get(3).getTimestamp(), equalTo(today - 6 * day)); + assertThat(list.get(7).getTimestamp(), equalTo(today - 10 * day)); + } + + @Test + public void testInvalidateNewerThan() + { + List records = getAllRecords(); + assertThat(records.size(), equalTo(121)); + + checkmarks.invalidateNewerThan(today - 20 * day); + + records = getAllRecords(); + assertThat(records.size(), equalTo(100)); + assertThat(records.get(0).timestamp, equalTo(today - 21 * day)); + } + + private List getAllRecords() + { + return new Select() + .from(CheckmarkRecord.class) + .where("habit = ?", habit.getId()) + .orderBy("timestamp desc") + .execute(); + } + +} 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 new file mode 100644 index 000000000..6752e483b --- /dev/null +++ b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteHabitListTest.java @@ -0,0 +1,229 @@ +/* + * 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.sqlite; + +import android.support.test.runner.*; +import android.test.suitebuilder.annotation.*; + +import com.activeandroid.query.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; +import org.junit.*; +import org.junit.rules.*; +import org.junit.runner.*; + +import java.util.*; + +import static junit.framework.Assert.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.core.IsEqual.*; + +@SuppressWarnings("JavaDoc") +@RunWith(AndroidJUnit4.class) +@MediumTest +public class SQLiteHabitListTest extends BaseAndroidTest +{ + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Override + public void setUp() + { + super.setUp(); + fixtures.purgeHabits(habitList); + + for (int i = 0; i < 10; i++) + { + Habit h = new Habit(); + h.setName("habit " + i); + h.setId((long) i); + if (i % 2 == 0) h.setArchived(1); + + HabitRecord record = new HabitRecord(); + record.copyFrom(h); + record.position = i; + record.save(i); + } + } + + @Test + public void testAdd_withDuplicate() + { + Habit habit = new Habit(); + habitList.add(habit); + exception.expect(IllegalArgumentException.class); + habitList.add(habit); + } + + @Test + public void testAdd_withId() + { + Habit habit = new Habit(); + habit.setName("Hello world with id"); + habit.setId(12300L); + + habitList.add(habit); + assertThat(habit.getId(), equalTo(12300L)); + + HabitRecord record = getRecord(12300L); + assertNotNull(record); + assertThat(record.name, equalTo(habit.getName())); + } + + @Test + public void testAdd_withoutId() + { + Habit habit = new Habit(); + habit.setName("Hello world"); + assertNull(habit.getId()); + + habitList.add(habit); + assertNotNull(habit.getId()); + + HabitRecord record = getRecord(habit.getId()); + assertNotNull(record); + assertThat(record.name, equalTo(habit.getName())); + } + + @Test + public void testCountActive() + { + assertThat(habitList.countActive(), equalTo(5)); + } + + @Test + public void testCountWithArchived() + { + assertThat(habitList.countWithArchived(), equalTo(10)); + } + + @Test + public void testGetAll_withArchived() + { + List habits = habitList.getAll(true); + assertThat(habits.size(), equalTo(10)); + assertThat(habits.get(3).getName(), equalTo("habit 3")); + } + + @Test + public void testGetAll_withoutArchived() + { + List habits = habitList.getAll(false); + assertThat(habits.size(), equalTo(5)); + assertThat(habits.get(3).getName(), equalTo("habit 7")); + + List another = habitList.getAll(false); + assertThat(habits, equalTo(another)); + } + + @Test + public void testGetById() + { + Habit h1 = habitList.getById(0); + assertNotNull(h1); + assertThat(h1.getName(), equalTo("habit 0")); + + Habit h2 = habitList.getById(0); + assertNotNull(h2); + assertThat(h1, equalTo(h2)); + } + + @Test + public void testGetById_withInvalid() + { + long invalidId = 9183792001L; + Habit h1 = habitList.getById(invalidId); + assertNull(h1); + } + + @Test + public void testGetByPosition() + { + Habit h = habitList.getByPosition(5); + assertNotNull(h); + assertThat(h.getName(), equalTo("habit 5")); + + h = habitList.getByPosition(5000); + assertNull(h); + } + + @Test + public void testIndexOf() + { + Habit h1 = habitList.getByPosition(5); + assertNotNull(h1); + assertThat(habitList.indexOf(h1), equalTo(5)); + + Habit h2 = new Habit(); + assertThat(habitList.indexOf(h2), equalTo(-1)); + + h2.setId(1000L); + assertThat(habitList.indexOf(h2), equalTo(-1)); + } + + @Test + public void test_reorder() + { + // Same as HabitListTest.java + // TODO: remove duplication + + int operations[][] = { + {5, 2}, {3, 7}, {4, 4}, {3, 2} + }; + + int expectedPosition[][] = { + {0, 1, 3, 4, 5, 2, 6, 7, 8, 9}, + {0, 1, 7, 3, 4, 2, 5, 6, 8, 9}, + {0, 1, 7, 3, 4, 2, 5, 6, 8, 9}, + {0, 1, 7, 2, 4, 3, 5, 6, 8, 9}, + }; + + for (int i = 0; i < operations.length; i++) + { + int from = operations[i][0]; + int to = operations[i][1]; + + Habit fromHabit = habitList.getByPosition(from); + Habit toHabit = habitList.getByPosition(to); + habitList.reorder(fromHabit, toHabit); + + int actualPositions[] = new int[10]; + + for (int j = 0; j < 10; j++) + { + Habit h = habitList.getById(j); + assertNotNull(h); + actualPositions[j] = habitList.indexOf(h); + } + + assertThat(actualPositions, equalTo(expectedPosition[i])); + } + } + + private HabitRecord getRecord(long id) + { + return new Select() + .from(HabitRecord.class) + .where("id = ?", id) + .executeSingle(); + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..ee2a579d5 --- /dev/null +++ b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteRepetitionListTest.java @@ -0,0 +1,145 @@ +/* + * 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.sqlite; + +import android.support.annotation.*; +import android.support.test.runner.*; +import android.test.suitebuilder.annotation.*; + +import com.activeandroid.query.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; +import org.isoron.uhabits.utils.*; +import org.junit.*; +import org.junit.runner.*; + +import java.util.*; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.hamcrest.core.IsNot.not; + +@RunWith(AndroidJUnit4.class) +@MediumTest +public class SQLiteRepetitionListTest extends BaseAndroidTest +{ + private Habit habit; + + private long today; + + private RepetitionList repetitions; + + private long day; + + @Override + public void setUp() + { + super.setUp(); + + habit = fixtures.createLongHabit(); + repetitions = habit.getRepetitions(); + today = DateUtils.getStartOfToday(); + day = DateUtils.millisecondsInOneDay; + } + + @Test + public void testAdd() + { + RepetitionRecord record = getByTimestamp(today + day); + assertThat(record, is(nullValue())); + + Repetition rep = new Repetition(habit, today + day); + habit.getRepetitions().add(rep); + + record = getByTimestamp(today + day); + assertThat(record, is(not(nullValue()))); + } + + @Test + public void testGetByInterval() + { + List reps = + repetitions.getByInterval(today - 10 * day, today); + + assertThat(reps.size(), equalTo(8)); + assertThat(reps.get(0).getTimestamp(), equalTo(today - 10 * day)); + assertThat(reps.get(4).getTimestamp(), equalTo(today - 5 * day)); + assertThat(reps.get(5).getTimestamp(), equalTo(today - 3 * day)); + } + + @Test + public void testGetByTimestamp() + { + 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); + assertThat(rep, is(nullValue())); + } + + @Test + public void testGetOldest() + { + Repetition rep = repetitions.getOldest(); + assertThat(rep, is(not(nullValue()))); + assertThat(rep.getHabit(), equalTo(habit)); + assertThat(rep.getTimestamp(), equalTo(today - 120 * day)); + } + + @Test + public void testGetOldest_withEmptyHabit() + { + Habit empty = fixtures.createEmptyHabit(); + Repetition rep = empty.getRepetitions().getOldest(); + assertThat(rep, is(nullValue())); + } + + @Test + public void testRemove() + { + RepetitionRecord record = getByTimestamp(today); + assertThat(record, is(not(nullValue()))); + + Repetition rep = record.toRepetition(); + repetitions.remove(rep); + + record = getByTimestamp(today); + assertThat(record, is(nullValue())); + } + + @Nullable + private RepetitionRecord getByTimestamp(long timestamp) + { + return selectByTimestamp(timestamp).executeSingle(); + } + + @NonNull + private From selectByTimestamp(long timestamp) + { + return new Select() + .from(RepetitionRecord.class) + .where("habit = ?", habit.getId()) + .and("timestamp = ?", timestamp); + } +} 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 new file mode 100644 index 000000000..8011b147f --- /dev/null +++ b/app/src/androidTest/java/org/isoron/uhabits/models/sqlite/SQLiteScoreListTest.java @@ -0,0 +1,126 @@ +/* + * 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.sqlite; + +import android.support.test.runner.*; +import android.test.suitebuilder.annotation.*; + +import com.activeandroid.query.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; +import org.isoron.uhabits.utils.*; +import org.junit.*; +import org.junit.runner.*; + +import java.util.*; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; + +@SuppressWarnings("JavaDoc") +@RunWith(AndroidJUnit4.class) +@MediumTest +public class SQLiteScoreListTest extends BaseAndroidTest +{ + private Habit habit; + + private ScoreList scores; + + private long today; + + private long day; + + @Override + public void setUp() + { + super.setUp(); + + habit = fixtures.createLongHabit(); + scores = habit.getScores(); + + today = DateUtils.getStartOfToday(); + day = DateUtils.millisecondsInOneDay; + } + + @Test + public void testGetAll() + { + List list = scores.getAll(); + assertThat(list.size(), equalTo(121)); + assertThat(list.get(0).getTimestamp(), equalTo(today)); + assertThat(list.get(10).getTimestamp(), equalTo(today - 10 * day)); + } + + @Test + public void testInvalidateNewerThan() + { + scores.getTodayValue(); // force recompute + List records = getAllRecords(); + assertThat(records.size(), equalTo(121)); + + scores.invalidateNewerThan(today - 10 * day); + + records = getAllRecords(); + assertThat(records.size(), equalTo(110)); + assertThat(records.get(0).timestamp, equalTo(today - 11 * day)); + } + + @Test + public void testAdd() + { + 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)); + + scores.add(list); + + List records = getAllRecords(); + assertThat(records.size(), equalTo(3)); + assertThat(records.get(0).timestamp, equalTo(today)); + } + + @Test + public void testGetByTimestamp() + { + Score s = scores.getByTimestamp(today); + assertNotNull(s); + assertThat(s.getTimestamp(), equalTo(today)); + + s = scores.getByTimestamp(today - 200 * day); + assertNull(s); + } + + private List getAllRecords() + { + return new Select() + .from(ScoreRecord.class) + .where("habit = ?", habit.getId()) + .orderBy("timestamp desc") + .execute(); + } + +} diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/MainActivityActions.java b/app/src/androidTest/java/org/isoron/uhabits/ui/MainActivityActions.java index 5b63b0b5b..c37e50308 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/MainActivityActions.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/MainActivityActions.java @@ -23,7 +23,7 @@ import android.support.test.espresso.NoMatchingViewException; import android.support.test.espresso.contrib.RecyclerViewActions; import org.isoron.uhabits.R; -import org.isoron.uhabits.models.sqlite.HabitRecord; +import org.isoron.uhabits.models.sqlite.records.HabitRecord; import java.util.Collections; import java.util.LinkedList; diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java index 9fba71854..eba6b19db 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java @@ -30,7 +30,7 @@ import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.LargeTest; import org.isoron.uhabits.R; -import org.isoron.uhabits.models.sqlite.HabitRecord; +import org.isoron.uhabits.models.sqlite.records.HabitRecord; import org.isoron.uhabits.utils.DateUtils; import org.isoron.uhabits.MainActivity; import org.junit.After; diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ArchiveHabitsCommandTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ArchiveHabitsCommandTest.java index 57a43817a..7c0b492f0 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ArchiveHabitsCommandTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ArchiveHabitsCommandTest.java @@ -25,7 +25,6 @@ import android.test.suitebuilder.annotation.SmallTest; import org.isoron.uhabits.BaseAndroidTest; import org.isoron.uhabits.commands.ArchiveHabitsCommand; import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.unit.HabitFixtures; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,7 +47,7 @@ public class ArchiveHabitsCommandTest extends BaseAndroidTest { super.setUp(); - habit = habitFixtures.createShortHabit(); + habit = fixtures.createShortHabit(); command = new ArchiveHabitsCommand(Collections.singletonList(habit)); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ChangeHabitColorCommandTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ChangeHabitColorCommandTest.java index bfff4f81b..c41656c29 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ChangeHabitColorCommandTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ChangeHabitColorCommandTest.java @@ -25,7 +25,6 @@ import android.test.suitebuilder.annotation.SmallTest; import org.isoron.uhabits.BaseAndroidTest; import org.isoron.uhabits.commands.ChangeHabitColorCommand; import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.unit.HabitFixtures; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -51,7 +50,7 @@ public class ChangeHabitColorCommandTest extends BaseAndroidTest for(int i = 0; i < 3; i ++) { - Habit habit = habitFixtures.createShortHabit(); + Habit habit = fixtures.createShortHabit(); habit.setColor(i + 1); habits.add(habit); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/CreateHabitCommandTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/CreateHabitCommandTest.java index c5a9f39f2..3c3f9ebfb 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/CreateHabitCommandTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/CreateHabitCommandTest.java @@ -23,7 +23,6 @@ import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import org.isoron.uhabits.BaseAndroidTest; -import org.isoron.uhabits.HabitsApplication; import org.isoron.uhabits.commands.CreateHabitCommand; import org.isoron.uhabits.models.Habit; import org.junit.Before; @@ -54,7 +53,7 @@ public class CreateHabitCommandTest extends BaseAndroidTest model.setName("New habit"); command = new CreateHabitCommand(model); - habitFixtures.purgeHabits(habitList); + fixtures.purgeHabits(habitList); } @Test diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/DeleteHabitsCommandTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/DeleteHabitsCommandTest.java index 983448f41..565a5fdd9 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/DeleteHabitsCommandTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/DeleteHabitsCommandTest.java @@ -19,22 +19,20 @@ package org.isoron.uhabits.unit.commands; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.runner.*; +import android.test.suitebuilder.annotation.*; -import org.isoron.uhabits.BaseAndroidTest; -import org.isoron.uhabits.commands.DeleteHabitsCommand; -import org.isoron.uhabits.models.Habit; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; +import org.isoron.uhabits.*; +import org.isoron.uhabits.commands.*; +import org.isoron.uhabits.models.*; +import org.junit.*; +import org.junit.rules.*; +import org.junit.runner.*; -import java.util.LinkedList; +import java.util.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; @RunWith(AndroidJUnit4.class) @SmallTest @@ -53,18 +51,18 @@ public class DeleteHabitsCommandTest extends BaseAndroidTest { super.setUp(); - habitFixtures.purgeHabits(habitList); + fixtures.purgeHabits(habitList); habits = new LinkedList<>(); // Habits that should be deleted for (int i = 0; i < 3; i++) { - Habit habit = habitFixtures.createShortHabit(); + Habit habit = fixtures.createShortHabit(); habits.add(habit); } // Extra habit that should not be deleted - Habit extraHabit = habitFixtures.createShortHabit(); + Habit extraHabit = fixtures.createShortHabit(); extraHabit.setName("extra"); command = new DeleteHabitsCommand(habits); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/EditHabitCommandTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/EditHabitCommandTest.java index 46f077481..a196c064d 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/EditHabitCommandTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/EditHabitCommandTest.java @@ -50,7 +50,7 @@ public class EditHabitCommandTest extends BaseAndroidTest { super.setUp(); - habit = habitFixtures.createShortHabit(); + habit = fixtures.createShortHabit(); habit.setName("original"); habit.setFreqDen(1); habit.setFreqNum(1); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ToggleRepetitionCommandTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ToggleRepetitionCommandTest.java index e14c35ad9..934d8e654 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ToggleRepetitionCommandTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ToggleRepetitionCommandTest.java @@ -47,7 +47,7 @@ public class ToggleRepetitionCommandTest extends BaseAndroidTest { super.setUp(); - habit = habitFixtures.createShortHabit(); + habit = fixtures.createShortHabit(); today = DateUtils.getStartOfToday(); command = new ToggleRepetitionCommand(habit, today); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/UnarchiveHabitsCommandTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/UnarchiveHabitsCommandTest.java index 07cc11104..d9235599b 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/UnarchiveHabitsCommandTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/UnarchiveHabitsCommandTest.java @@ -47,7 +47,7 @@ public class UnarchiveHabitsCommandTest extends BaseAndroidTest { super.setUp(); - habit = habitFixtures.createShortHabit(); + habit = fixtures.createShortHabit(); habit.setArchived(1); habitList.update(habit); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/io/HabitsCSVExporterTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/io/HabitsCSVExporterTest.java index a965a7eb9..d4e35f1e5 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/io/HabitsCSVExporterTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/io/HabitsCSVExporterTest.java @@ -53,9 +53,9 @@ public class HabitsCSVExporterTest extends BaseAndroidTest { super.setUp(); - habitFixtures.purgeHabits(habitList); - habitFixtures.createShortHabit(); - habitFixtures.createEmptyHabit(); + fixtures.purgeHabits(habitList); + fixtures.createShortHabit(); + fixtures.createEmptyHabit(); Context targetContext = InstrumentationRegistry.getTargetContext(); baseDir = targetContext.getCacheDir(); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java index 4f73471b0..f5e113f9b 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java @@ -59,7 +59,7 @@ public class ImportTest extends BaseAndroidTest super.setUp(); DateUtils.setFixedLocalTime(null); - habitFixtures.purgeHabits(habitList); + fixtures.purgeHabits(habitList); context = InstrumentationRegistry.getInstrumentation().getContext(); baseDir = FileUtils.getFilesDir("Backups"); if(baseDir == null) fail("baseDir should not be null"); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreListTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreListTest.java deleted file mode 100644 index d64cd3aea..000000000 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreListTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.unit.models; - -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; - -import org.isoron.uhabits.BaseAndroidTest; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ScoreListTest extends BaseAndroidTest -{ - -} diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportCSVTaskTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportCSVTaskTest.java index 51cb53dae..d688a34b2 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportCSVTaskTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportCSVTaskTest.java @@ -19,24 +19,21 @@ package org.isoron.uhabits.unit.tasks; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.runner.*; +import android.test.suitebuilder.annotation.*; -import org.isoron.uhabits.BaseAndroidTest; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.tasks.ExportCSVTask; -import org.isoron.uhabits.unit.HabitFixtures; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.tasks.*; +import org.junit.*; +import org.junit.runner.*; -import java.io.File; -import java.util.List; +import java.io.*; +import java.util.*; -import static junit.framework.Assert.assertTrue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; +import static junit.framework.Assert.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; import static org.hamcrest.core.IsNot.not; @RunWith(AndroidJUnit4.class) @@ -52,7 +49,7 @@ public class ExportCSVTaskTest extends BaseAndroidTest @Test public void testExportCSV() throws Throwable { - habitFixtures.createShortHabit(); + fixtures.createShortHabit(); List habits = habitList.getAll(true); ExportCSVTask task = new ExportCSVTask(habits, null); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkWidgetViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkWidgetViewTest.java index a5eb79627..f6f283f74 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkWidgetViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkWidgetViewTest.java @@ -47,7 +47,7 @@ public class CheckmarkWidgetViewTest extends ViewTest super.setUp(); InterfaceUtils.setFixedTheme(R.style.TransparentWidgetTheme); - habit = habitFixtures.createShortHabit(); + habit = fixtures.createShortHabit(); view = new CheckmarkWidgetView(targetContext); view.setHabit(habit); refreshData(view); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitFrequencyViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitFrequencyViewTest.java index f43578699..45f6c8b8a 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitFrequencyViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitFrequencyViewTest.java @@ -39,8 +39,8 @@ public class HabitFrequencyViewTest extends ViewTest { super.setUp(); - habitFixtures.purgeHabits(habitList); - Habit habit = habitFixtures.createLongHabit(); + fixtures.purgeHabits(habitList); + Habit habit = fixtures.createLongHabit(); view = new HabitFrequencyView(targetContext); view.setHabit(habit); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java index f2dcb0721..9a4344775 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java @@ -47,8 +47,8 @@ public class HabitHistoryViewTest extends ViewTest { super.setUp(); - habitFixtures.purgeHabits(habitList); - habit = habitFixtures.createLongHabit(); + fixtures.purgeHabits(habitList); + habit = fixtures.createLongHabit(); view = new HabitHistoryView(targetContext); view.setHabit(habit); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java index 31c0d84a0..0bf209502 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java @@ -42,8 +42,8 @@ public class HabitScoreViewTest extends ViewTest { super.setUp(); - habitFixtures.purgeHabits(habitList); - habit = habitFixtures.createLongHabit(); + fixtures.purgeHabits(habitList); + habit = fixtures.createLongHabit(); view = new HabitScoreView(targetContext); view.setHabit(habit); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitStreakViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitStreakViewTest.java index 9cb86086f..276a798bf 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitStreakViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitStreakViewTest.java @@ -40,8 +40,8 @@ public class HabitStreakViewTest extends ViewTest { super.setUp(); - habitFixtures.purgeHabits(habitList); - Habit habit = habitFixtures.createLongHabit(); + fixtures.purgeHabits(habitList); + Habit habit = fixtures.createLongHabit(); view = new HabitStreakView(targetContext); measureView(dpToPixels(300), dpToPixels(100), view); 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 f7c712dfb..29348b0b7 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Checkmark.java +++ b/app/src/main/java/org/isoron/uhabits/models/Checkmark.java @@ -19,7 +19,7 @@ package org.isoron.uhabits.models; -import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.*; /** * A Checkmark represents the completion status of the habit for a given day. @@ -62,6 +62,11 @@ public class Checkmark this.value = value; } + public int compareNewer(Checkmark other) + { + return Long.signum(this.getTimestamp() - other.getTimestamp()); + } + public Habit getHabit() { return habit; 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 054aa35b3..89b05d63a 100644 --- a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java +++ b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java @@ -19,16 +19,13 @@ package org.isoron.uhabits.models; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.utils.*; -import java.io.IOException; -import java.io.Writer; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; +import java.io.*; +import java.text.*; +import java.util.*; /** * The collection of {@link Checkmark}s belonging to a habit. @@ -44,20 +41,30 @@ public abstract class CheckmarkList this.habit = habit; } + /** + * Adds all the given checkmarks to the list. + *

+ * This should never be called by the application, since the checkmarks are + * computed automatically from the list of repetitions. + * + * @param checkmarks the checkmarks to be added. + */ + public abstract void add(List checkmarks); + /** * Returns the values for all the checkmarks, since the oldest repetition of - * the habit until today. If there are no repetitions at all, returns an - * empty array. + * the habit until today. *

- * The values are returned in an array containing one integer value for each - * day since the first repetition of the habit until today. The first entry + * If there are no repetitions at all, returns an empty array. The values + * are returned in an array containing one integer value for each day since + * the first repetition of the habit until today. The first entry * corresponds to today, the second entry corresponds to yesterday, and so * on. * * @return values for the checkmarks in the interval */ @NonNull - public int[] getAllValues() + public final int[] getAllValues() { Repetition oldestRep = habit.getRepetitions().getOldest(); if (oldestRep == null) return new int[0]; @@ -68,17 +75,32 @@ public abstract class CheckmarkList return getValues(fromTimestamp, toTimestamp); } + /** + * Returns the list of checkmarks that fall within the given interval. + *

+ * There is exactly one checkmark per day in the interval. The endpoints of + * the interval are included. The list is ordered by timestamp (decreasing). + * That is, the first checkmark corresponds to the newest timestamp, and the + * last checkmark corresponds to the oldest timestamp. + * + * @param fromTimestamp timestamp of the beginning of the interval. + * @param toTimestamp timestamp of the end of the interval. + * @return the list of checkmarks within the interval. + */ + @NonNull + public abstract List getByInterval(long fromTimestamp, + long toTimestamp); + /** * Returns the checkmark for today. * * @return checkmark for today */ @Nullable - public Checkmark getToday() + public final Checkmark getToday() { - long today = DateUtils.getStartOfToday(); - compute(today, today); - return getNewest(); + computeAll(); + return getNewestComputed(); } /** @@ -86,7 +108,7 @@ public abstract class CheckmarkList * * @return value of today's checkmark */ - public int getTodayValue() + public final int getTodayValue() { Checkmark today = getToday(); if (today != null) return today.getValue(); @@ -106,7 +128,17 @@ public abstract class CheckmarkList * @param to timestamp for the newest checkmark * @return values for the checkmarks inside the given interval */ - public abstract int[] getValues(long from, long to); + public final int[] getValues(long from, long to) + { + List checkmarks = getByInterval(from, to); + int values[] = new int[checkmarks.size()]; + + int i = 0; + for (Checkmark c : checkmarks) + values[i++] = c.getValue(); + + return values; + } /** * Marks as invalid every checkmark that has timestamp either equal or newer @@ -119,13 +151,11 @@ public abstract class CheckmarkList /** * Writes the entire list of checkmarks to the given writer, in CSV format. - * There is one line for each checkmark. Each line contains two fields: - * timestamp and value. * * @param out the writer where the CSV will be output * @throws IOException in case write operations fail */ - public void writeCSV(Writer out) throws IOException + public final void writeCSV(Writer out) throws IOException { computeAll(); @@ -149,11 +179,11 @@ public abstract class CheckmarkList * @param from timestamp for the beginning of the interval * @param to timestamp for the end of the interval */ - protected void compute(long from, final long to) + protected final void compute(long from, final long to) { final long day = DateUtils.millisecondsInOneDay; - Checkmark newestCheckmark = getNewest(); + Checkmark newestCheckmark = getNewestComputed(); if (newestCheckmark != null) from = newestCheckmark.getTimestamp() + day; @@ -185,12 +215,16 @@ public abstract class CheckmarkList checks[i] = Checkmark.CHECKED_IMPLICITLY; } + List checkmarks = new LinkedList<>(); - long timestamps[] = new long[nDays]; for (int i = 0; i < nDays; i++) - timestamps[i] = to - i * day; + { + int value = checks[i]; + long timestamp = to - i * day; + checkmarks.add(new Checkmark(habit, timestamp, value)); + } - insert(timestamps, checks); + add(checkmarks); } /** @@ -198,24 +232,21 @@ public abstract class CheckmarkList * repetition until today. Days that already have a corresponding checkmark * are skipped. */ - protected void computeAll() + protected final void computeAll() { Repetition oldest = habit.getRepetitions().getOldest(); if (oldest == null) return; Long today = DateUtils.getStartOfToday(); - compute(oldest.getTimestamp(), today); } /** - * Returns newest checkmark that has already been computed. Ignores any - * checkmark that has timestamp in the future. This does not update the - * cache. + * Returns newest checkmark that has already been computed. + *

+ * Ignores any checkmark that has timestamp in the future. * * @return newest checkmark already computed */ - protected abstract Checkmark getNewest(); - - protected abstract void insert(long timestamps[], int values[]); + protected abstract Checkmark getNewestComputed(); } 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 aeaf152b1..7b0e57ff4 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Habit.java +++ b/app/src/main/java/org/isoron/uhabits/models/Habit.java @@ -19,17 +19,16 @@ package org.isoron.uhabits.models; -import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.net.*; +import android.support.annotation.*; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.isoron.uhabits.HabitsApplication; -import org.isoron.uhabits.utils.DateUtils; +import org.apache.commons.lang3.builder.*; +import org.isoron.uhabits.*; +import org.isoron.uhabits.utils.*; -import java.util.Locale; +import java.util.*; -import javax.inject.Inject; +import javax.inject.*; /** * The thing that the user wants to track. @@ -105,7 +104,7 @@ public class Habit checkmarks = factory.buildCheckmarkList(this); streaks = factory.buildStreakList(this); scores = factory.buildScoreList(this); - repetitions = factory.buidRepetitionList(this); + repetitions = factory.buildRepetitionList(this); } /** @@ -128,7 +127,7 @@ public class Habit checkmarks = factory.buildCheckmarkList(this); streaks = factory.buildStreakList(this); scores = factory.buildScoreList(this); - repetitions = factory.buidRepetitionList(this); + repetitions = factory.buildRepetitionList(this); } /** @@ -235,7 +234,7 @@ public class Habit return freqNum; } - public void setFreqNum(Integer freqNum) + public void setFreqNum(@NonNull Integer freqNum) { this.freqNum = freqNum; } @@ -243,16 +242,18 @@ public class Habit /** * Not currently used. */ + @NonNull public Integer getHighlight() { return highlight; } - public void setHighlight(Integer highlight) + public void setHighlight(@NonNull Integer highlight) { this.highlight = highlight; } + @Nullable public Long getId() { return id; @@ -387,7 +388,7 @@ public class Habit return archived != 0; } - public void setArchived(Integer archived) + public void setArchived(@NonNull Integer archived) { this.archived = archived; } 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 946aab86b..4b80e7383 100644 --- a/app/src/main/java/org/isoron/uhabits/models/HabitList.java +++ b/app/src/main/java/org/isoron/uhabits/models/HabitList.java @@ -19,18 +19,14 @@ package org.isoron.uhabits.models; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import com.opencsv.CSVWriter; +import com.opencsv.*; -import org.isoron.uhabits.utils.ColorUtils; +import org.isoron.uhabits.utils.*; -import java.io.IOException; -import java.io.Writer; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.io.*; +import java.util.*; /** * An ordered collection of {@link Habit}s. @@ -43,7 +39,8 @@ public abstract class HabitList * Creates a new HabitList. *

* Depending on the implementation, this list can either be empty or be - * populated by some pre-existing habits. + * populated by some pre-existing habits, for example, from a certain + * database. */ public HabitList() { @@ -52,17 +49,24 @@ public abstract class HabitList /** * Inserts a new habit in the list. + *

+ * If the id of the habit is null, the list will assign it a new id, which + * is guaranteed to be unique in the scope of the list. If id is not null, + * the caller should make sure that the list does not already contain + * another habit with same id, otherwise a RuntimeException will be thrown. * * @param habit the habit to be inserted + * @throws IllegalArgumentException if the habit is already on the list. */ - public abstract void add(@NonNull Habit habit); + public abstract void add(@NonNull Habit habit) + throws IllegalArgumentException; /** - * Returns the total number of unarchived habits. + * Returns the total number of active habits. * - * @return number of unarchived habits + * @return number of active habits */ - public abstract int count(); + public abstract int countActive(); /** * Returns the total number of habits, including archived habits. diff --git a/app/src/main/java/org/isoron/uhabits/models/ModelFactory.java b/app/src/main/java/org/isoron/uhabits/models/ModelFactory.java index c1300ac71..6df0002c0 100644 --- a/app/src/main/java/org/isoron/uhabits/models/ModelFactory.java +++ b/app/src/main/java/org/isoron/uhabits/models/ModelFactory.java @@ -20,17 +20,17 @@ package org.isoron.uhabits.models; /** - * Interface implemented by factories that provide concrete implementations - * of the core model classes. + * Interface implemented by factories that provide concrete implementations of + * the core model classes. */ public interface ModelFactory { - RepetitionList buidRepetitionList(Habit habit); - - HabitList buildHabitList(); + RepetitionList buildRepetitionList(Habit habit); CheckmarkList buildCheckmarkList(Habit habit); + HabitList buildHabitList(); + ScoreList buildScoreList(Habit habit); StreakList buildStreakList(Habit habit); 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 ffd8a8499..001bf2695 100644 --- a/app/src/main/java/org/isoron/uhabits/models/ModelObservable.java +++ b/app/src/main/java/org/isoron/uhabits/models/ModelObservable.java @@ -19,8 +19,7 @@ package org.isoron.uhabits.models; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * A ModelObservable allows objects to subscribe themselves to it and receive 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 b3e8b2771..bcfb8bc65 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Repetition.java +++ b/app/src/main/java/org/isoron/uhabits/models/Repetition.java @@ -19,9 +19,9 @@ package org.isoron.uhabits.models; -import android.support.annotation.NonNull; +import android.support.annotation.*; -import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.*; /** * Represents a record that the user has performed a certain habit at a certain 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 aa0511c0a..d4f38e5e3 100644 --- a/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java +++ b/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java @@ -19,15 +19,12 @@ package org.isoron.uhabits.models; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.utils.*; -import java.util.Arrays; -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; +import java.util.*; /** * The collection of {@link Repetition}s belonging to a habit. @@ -72,15 +69,16 @@ public abstract class RepetitionList /** * Returns the list of repetitions that happened within the given time * interval. - * - * The list is sorted by timestamp in decreasing order. That is, the first - * element corresponds to the most recent timestamp. The endpoints of the - * interval are included. + *

+ * The list is sorted by timestamp in increasing order. That is, the first + * element corresponds to oldest timestamp, while the last element + * corresponds to the newest. The endpoints of the interval are included. * * @param fromTimestamp timestamp of the beginning of the interval * @param toTimestamp timestamp of the end of the interval * @return list of repetitions within given time interval */ + // TODO: Change order timestamp desc public abstract List getByInterval(long fromTimestamp, long toTimestamp); 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 2ad7b5aae..e4f67c34c 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Score.java +++ b/app/src/main/java/org/isoron/uhabits/models/Score.java @@ -19,13 +19,18 @@ package org.isoron.uhabits.models; -import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.*; /** * Represents how strong a habit is at a certain date. */ public class Score { + /** + * Maximum score value attainable by any habit. + */ + public static final int MAX_VALUE = 19259478; + /** * Habit to which this score belongs to. */ @@ -42,11 +47,6 @@ public class Score */ private final Integer value; - /** - * Maximum score value attainable by any habit. - */ - public static final int MAX_VALUE = 19259478; - public Score(Habit habit, Long timestamp, Integer value) { this.habit = habit; 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 ec4ae3d81..983e6c11d 100644 --- a/app/src/main/java/org/isoron/uhabits/models/ScoreList.java +++ b/app/src/main/java/org/isoron/uhabits/models/ScoreList.java @@ -19,21 +19,15 @@ package org.isoron.uhabits.models; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.utils.*; -import java.io.IOException; -import java.io.Writer; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; +import java.io.*; +import java.text.*; +import java.util.*; -public abstract class ScoreList +public abstract class ScoreList implements Iterable { protected final Habit habit; @@ -53,8 +47,29 @@ public abstract class ScoreList observable = new ModelObservable(); } + /** + * Adds the given scores to the list. + *

+ * This method should not be called by the application, since the scores are + * computed automatically from the list of repetitions. + * + * @param scores the scores to add. + */ + public abstract void add(List scores); + public abstract List getAll(); + /** + * Returns the score that has the given timestamp. + *

+ * If no such score exists, returns null. + * + * @param timestamp the timestamp to find. + * @return the score with given timestamp, or null if none exists. + */ + @Nullable + public abstract Score getByTimestamp(long timestamp); + public ModelObservable getObservable() { return observable; @@ -72,11 +87,20 @@ public abstract class ScoreList /** * Returns the value of the score for a given day. + *

+ * If there is no score at the given timestamp (for example, if the + * timestamp given happens before the first repetition of the habit) then + * returns zero. * * @param timestamp the timestamp of a day - * @return score for that day + * @return score value for that day */ - public abstract int getValue(long timestamp); + public final int getValue(long timestamp) + { + Score s = getByTimestamp(timestamp); + if (s != null) return s.getValue(); + return 0; + } public List groupBy(DateUtils.TruncateField field) { @@ -95,12 +119,18 @@ public abstract class ScoreList */ public abstract void invalidateNewerThan(long timestamp); + @Override + public Iterator iterator() + { + return getAll().iterator(); + } + public void writeCSV(Writer out) throws IOException { computeAll(); SimpleDateFormat dateFormat = DateUtils.getCSVDateFormat(); - for (Score s : getAll()) + for (Score s : this) { String timestamp = dateFormat.format(s.getTimestamp()); String score = @@ -109,8 +139,6 @@ public abstract class ScoreList } } - protected abstract void add(List scores); - /** * Computes and saves the scores that are missing inside a given time * interval. @@ -173,20 +201,21 @@ public abstract class ScoreList } /** - * Returns the score for a certain day. + * Returns the most recent score that has already been computed. + *

+ * If no score has been computed yet, returns null. * - * @param timestamp the timestamp for the day - * @return the score for the day + * @return the newest score computed, or null if none exist */ @Nullable - protected abstract Score get(long timestamp); + protected abstract Score getNewestComputed(); @NonNull private HashMap> getGroupedValues(DateUtils.TruncateField field) { HashMap> groups = new HashMap<>(); - for (Score s : getAll()) + for (Score s : this) { long groupTimestamp = DateUtils.truncate(field, s.getTimestamp()); @@ -199,17 +228,6 @@ public abstract class ScoreList return groups; } - /** - * Returns the most recent score that has already been computed. - *

- * If no score has been computed yet, returns null. - * - * @return the newest score computed, or null if none exist - */ - @Nullable - protected abstract Score getNewestComputed(); - - @NonNull private List groupsToAvgScores(HashMap> groups) { 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 53854a798..107e5fe86 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Streak.java +++ b/app/src/main/java/org/isoron/uhabits/models/Streak.java @@ -19,8 +19,8 @@ package org.isoron.uhabits.models; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.isoron.uhabits.utils.DateUtils; +import org.apache.commons.lang3.builder.*; +import org.isoron.uhabits.utils.*; public class Streak { 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 870a4f16e..c26b3e284 100644 --- a/app/src/main/java/org/isoron/uhabits/models/StreakList.java +++ b/app/src/main/java/org/isoron/uhabits/models/StreakList.java @@ -19,15 +19,11 @@ package org.isoron.uhabits.models; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.utils.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * The collection of {@link Streak}s that belong to a habit. @@ -80,7 +76,7 @@ public abstract class StreakList List streaks = checkmarksToStreaks(beginning, checks); removeNewestComputed(); - insert(streaks); + add(streaks); } /** @@ -155,7 +151,7 @@ public abstract class StreakList return list; } - protected abstract void insert(@NonNull List streaks); + protected abstract void add(@NonNull List streaks); protected abstract void removeNewestComputed(); } diff --git a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryCheckmarkList.java b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryCheckmarkList.java index f42879deb..d8d45fb2a 100644 --- a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryCheckmarkList.java +++ b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryCheckmarkList.java @@ -19,13 +19,11 @@ package org.isoron.uhabits.models.memory; -import org.isoron.uhabits.models.Checkmark; -import org.isoron.uhabits.models.CheckmarkList; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.utils.DateUtils; +import android.support.annotation.*; -import java.util.Collections; -import java.util.LinkedList; +import org.isoron.uhabits.models.*; + +import java.util.*; /** * In-memory implementation of {@link CheckmarkList}. @@ -41,20 +39,25 @@ public class MemoryCheckmarkList extends CheckmarkList } @Override - public int[] getValues(long from, long to) + public void add(List checkmarks) + { + list.addAll(checkmarks); + Collections.sort(list, (c1, c2) -> c2.compareNewer(c1)); + } + + @NonNull + @Override + public List getByInterval(long fromTimestamp, long toTimestamp) { - compute(from, to); - if (from > to) return new int[0]; + compute(fromTimestamp, toTimestamp); - int length = (int) ((to - from) / DateUtils.millisecondsInOneDay + 1); - int values[] = new int[length]; + List filtered = new LinkedList<>(); - int k = 0; for (Checkmark c : list) - if(c.getTimestamp() >= from && c.getTimestamp() <= to) - values[k++] = c.getValue(); + if (c.getTimestamp() >= fromTimestamp && + c.getTimestamp() <= toTimestamp) filtered.add(c); - return values; + return filtered; } @Override @@ -69,7 +72,7 @@ public class MemoryCheckmarkList extends CheckmarkList } @Override - protected Checkmark getNewest() + protected Checkmark getNewestComputed() { long newestTimestamp = 0; Checkmark newestCheck = null; @@ -86,17 +89,4 @@ public class MemoryCheckmarkList extends CheckmarkList return newestCheck; } - @Override - protected void insert(long[] timestamps, int[] values) - { - for (int i = 0; i < timestamps.length; i++) - { - long t = timestamps[i]; - int v = values[i]; - list.add(new Checkmark(habit, t, v)); - } - - Collections.sort(list, - (c1, c2) -> (int) (c2.getTimestamp() - c1.getTimestamp())); - } } diff --git a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryHabitList.java b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryHabitList.java index deee26b0d..d0f76dbce 100644 --- a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryHabitList.java +++ b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryHabitList.java @@ -19,14 +19,11 @@ package org.isoron.uhabits.models.memory; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.HabitList; +import org.isoron.uhabits.models.*; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * In-memory implementation of {@link HabitList}. @@ -42,13 +39,21 @@ public class MemoryHabitList extends HabitList } @Override - public void add(Habit habit) + public void add(@NonNull Habit habit) throws IllegalArgumentException { + if (list.contains(habit)) + throw new IllegalArgumentException("habit already added"); + + Long id = habit.getId(); + if (id != null && getById(id) != null) + throw new RuntimeException("duplicate id"); + + if (id == null) habit.setId((long) list.size()); list.addLast(habit); } @Override - public int count() + public int countActive() { int count = 0; for (Habit h : list) if (!h.isArchived()) count++; @@ -61,13 +66,6 @@ public class MemoryHabitList extends HabitList return list.size(); } - @Override - public Habit getById(long id) - { - for (Habit h : list) if (h.getId() == id) return h; - return null; - } - @NonNull @Override public List getAll(boolean includeArchive) @@ -76,6 +74,13 @@ public class MemoryHabitList extends HabitList return getFiltered(habit -> !habit.isArchived()); } + @Override + public Habit getById(long id) + { + for (Habit h : list) if (h.getId() == id) return h; + return null; + } + @Nullable @Override public Habit getByPosition(int position) @@ -84,7 +89,7 @@ public class MemoryHabitList extends HabitList } @Override - public int indexOf(Habit h) + public int indexOf(@NonNull Habit h) { return list.indexOf(h); } diff --git a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryModelFactory.java b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryModelFactory.java index baef4ae40..86e0eb9fe 100644 --- a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryModelFactory.java +++ b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryModelFactory.java @@ -19,18 +19,12 @@ package org.isoron.uhabits.models.memory; -import org.isoron.uhabits.models.CheckmarkList; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.HabitList; -import org.isoron.uhabits.models.ModelFactory; -import org.isoron.uhabits.models.RepetitionList; -import org.isoron.uhabits.models.ScoreList; -import org.isoron.uhabits.models.StreakList; +import org.isoron.uhabits.models.*; public class MemoryModelFactory implements ModelFactory { @Override - public RepetitionList buidRepetitionList(Habit habit) + public RepetitionList buildRepetitionList(Habit habit) { return new MemoryRepetitionList(habit); } diff --git a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryRepetitionList.java b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryRepetitionList.java index 13016e8f2..461de8021 100644 --- a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryRepetitionList.java +++ b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryRepetitionList.java @@ -19,16 +19,11 @@ package org.isoron.uhabits.models.memory; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Repetition; -import org.isoron.uhabits.models.RepetitionList; +import org.isoron.uhabits.models.*; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * In-memory implementation of {@link RepetitionList}. @@ -54,6 +49,7 @@ public class MemoryRepetitionList extends RepetitionList public List getByInterval(long fromTimestamp, long toTimestamp) { LinkedList filtered = new LinkedList<>(); + for (Repetition r : list) { long t = r.getTimestamp(); diff --git a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryScoreList.java b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryScoreList.java index 4dac68e3a..358415473 100644 --- a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryScoreList.java +++ b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryScoreList.java @@ -19,16 +19,11 @@ package org.isoron.uhabits.models.memory; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Score; -import org.isoron.uhabits.models.ScoreList; +import org.isoron.uhabits.models.*; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.util.*; public class MemoryScoreList extends ScoreList { @@ -40,14 +35,6 @@ public class MemoryScoreList extends ScoreList list = new LinkedList<>(); } - @Override - public int getValue(long timestamp) - { - Score s = get(timestamp); - if (s != null) return s.getValue(); - return 0; - } - @Override public void invalidateNewerThan(long timestamp) { @@ -67,17 +54,9 @@ public class MemoryScoreList extends ScoreList return new LinkedList<>(list); } - @Override - protected void add(List scores) - { - list.addAll(scores); - Collections.sort(list, - (s1, s2) -> Long.signum(s2.getTimestamp() - s1.getTimestamp())); - } - - @Override @Nullable - protected Score get(long timestamp) + @Override + public Score getByTimestamp(long timestamp) { computeAll(); for (Score s : list) @@ -86,11 +65,19 @@ public class MemoryScoreList extends ScoreList return null; } + @Override + public void add(List scores) + { + list.addAll(scores); + Collections.sort(list, + (s1, s2) -> Long.signum(s2.getTimestamp() - s1.getTimestamp())); + } + @Nullable @Override protected Score getNewestComputed() { - if(list.isEmpty()) return null; + if (list.isEmpty()) return null; return list.get(0); } } diff --git a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryStreakList.java b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryStreakList.java index 4efab4ecf..31fd3de8d 100644 --- a/app/src/main/java/org/isoron/uhabits/models/memory/MemoryStreakList.java +++ b/app/src/main/java/org/isoron/uhabits/models/memory/MemoryStreakList.java @@ -19,14 +19,10 @@ package org.isoron.uhabits.models.memory; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Streak; -import org.isoron.uhabits.models.StreakList; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.utils.*; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.util.*; public class MemoryStreakList extends StreakList { @@ -43,9 +39,8 @@ public class MemoryStreakList extends StreakList { Streak newest = null; - for(Streak s : list) - if(newest == null || s.getEnd() > newest.getEnd()) - newest = s; + for (Streak s : list) + if (newest == null || s.getEnd() > newest.getEnd()) newest = s; return newest; } @@ -55,8 +50,8 @@ public class MemoryStreakList extends StreakList { LinkedList discard = new LinkedList<>(); - for(Streak s : list) - if(s.getEnd() >= timestamp - DateUtils.millisecondsInOneDay) + for (Streak s : list) + if (s.getEnd() >= timestamp - DateUtils.millisecondsInOneDay) discard.add(s); list.removeAll(discard); @@ -64,7 +59,7 @@ public class MemoryStreakList extends StreakList } @Override - protected void insert(List streaks) + protected void add(List streaks) { list.addAll(streaks); Collections.sort(list, (s1, s2) -> s2.compareNewer(s1)); @@ -74,7 +69,7 @@ public class MemoryStreakList extends StreakList protected void removeNewestComputed() { Streak newest = getNewestComputed(); - if(newest != null) list.remove(newest); + if (newest != null) list.remove(newest); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLModelFactory.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLModelFactory.java index 48db5fe95..904391e36 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLModelFactory.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLModelFactory.java @@ -19,13 +19,7 @@ package org.isoron.uhabits.models.sqlite; -import org.isoron.uhabits.models.CheckmarkList; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.HabitList; -import org.isoron.uhabits.models.ModelFactory; -import org.isoron.uhabits.models.RepetitionList; -import org.isoron.uhabits.models.ScoreList; -import org.isoron.uhabits.models.StreakList; +import org.isoron.uhabits.models.*; /** * Factory that provides models backed by an SQLite database. @@ -33,7 +27,7 @@ import org.isoron.uhabits.models.StreakList; public class SQLModelFactory implements ModelFactory { @Override - public RepetitionList buidRepetitionList(Habit habit) + public RepetitionList buildRepetitionList(Habit habit) { return new SQLiteRepetitionList(habit); } @@ -47,7 +41,7 @@ public class SQLModelFactory implements ModelFactory @Override public HabitList buildHabitList() { - return new SQLiteHabitList(); + return SQLiteHabitList.getInstance(); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkList.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkList.java index ba059e938..d83808c8e 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkList.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteCheckmarkList.java @@ -19,20 +19,17 @@ package org.isoron.uhabits.models.sqlite; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteStatement; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.database.sqlite.*; +import android.support.annotation.*; -import com.activeandroid.Cache; -import com.activeandroid.query.Delete; -import com.activeandroid.query.Select; +import com.activeandroid.*; +import com.activeandroid.query.*; -import org.isoron.uhabits.models.Checkmark; -import org.isoron.uhabits.models.CheckmarkList; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; +import org.isoron.uhabits.utils.*; + +import java.util.*; /** * Implementation of a {@link CheckmarkList} that is backed by SQLite. @@ -44,6 +41,47 @@ public class SQLiteCheckmarkList extends CheckmarkList super(habit); } + @Override + public void add(List checkmarks) + { + String query = + "insert into Checkmarks(habit, timestamp, value) values (?,?,?)"; + + SQLiteDatabase db = Cache.openDatabase(); + db.beginTransaction(); + try + { + SQLiteStatement statement = db.compileStatement(query); + + for (Checkmark c : checkmarks) + { + statement.bindLong(1, habit.getId()); + statement.bindLong(2, c.getTimestamp()); + statement.bindLong(3, c.getValue()); + statement.execute(); + } + + db.setTransactionSuccessful(); + } + finally + { + db.endTransaction(); + } + } + + @Override + public List getByInterval(long fromTimestamp, long toTimestamp) + { + computeAll(); + + List records = select() + .and("timestamp >= ?", fromTimestamp) + .and("timestamp <= ?", toTimestamp) + .execute(); + + return toCheckmarks(records); + } + @Override public void invalidateNewerThan(long timestamp) { @@ -57,84 +95,29 @@ public class SQLiteCheckmarkList extends CheckmarkList } @Override - @NonNull - public int[] getValues(long fromTimestamp, long toTimestamp) + @Nullable + protected Checkmark getNewestComputed() { - compute(fromTimestamp, toTimestamp); - - if (fromTimestamp > toTimestamp) return new int[0]; - - String query = "select value, timestamp from Checkmarks where " + - "habit = ? and timestamp >= ? and timestamp <= ?"; - - SQLiteDatabase db = Cache.openDatabase(); - String args[] = { - habit.getId().toString(), - Long.toString(fromTimestamp), - Long.toString(toTimestamp) - }; - Cursor cursor = db.rawQuery(query, args); - - long day = DateUtils.millisecondsInOneDay; - int nDays = (int) ((toTimestamp - fromTimestamp) / day) + 1; - int[] checks = new int[nDays]; - - if (cursor.moveToFirst()) - { - do - { - long timestamp = cursor.getLong(1); - int offset = (int) ((timestamp - fromTimestamp) / day); - checks[nDays - offset - 1] = cursor.getInt(0); - - } while (cursor.moveToNext()); - } - - cursor.close(); - return checks; + CheckmarkRecord record = select().limit(1).executeSingle(); + if (record == null) return null; + return record.toCheckmark(); } - @Override - @Nullable - protected Checkmark getNewest() + @NonNull + private From select() { - CheckmarkRecord record = new Select() + return new Select() .from(CheckmarkRecord.class) .where("habit = ?", habit.getId()) .and("timestamp <= ?", DateUtils.getStartOfToday()) - .orderBy("timestamp desc") - .limit(1) - .executeSingle(); - - if(record == null) return null; - return record.toCheckmark(); + .orderBy("timestamp desc"); } - @Override - protected void insert(long timestamps[], int values[]) + @NonNull + private List toCheckmarks(@NonNull List records) { - String query = - "insert into Checkmarks(habit, timestamp, value) values (?,?,?)"; - - SQLiteDatabase db = Cache.openDatabase(); - db.beginTransaction(); - try - { - SQLiteStatement statement = db.compileStatement(query); - - for (int i = 0; i < timestamps.length; i++) - { - statement.bindLong(1, habit.getId()); - statement.bindLong(2, timestamps[i]); - statement.bindLong(3, values[i]); - statement.execute(); - } - - db.setTransactionSuccessful(); - } - finally - { - db.endTransaction(); - } + List checkmarks = new LinkedList<>(); + for (CheckmarkRecord r : records) checkmarks.add(r.toCheckmark()); + return checkmarks; } } diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteHabitList.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteHabitList.java index 34e97104e..9abd9213b 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteHabitList.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteHabitList.java @@ -19,19 +19,14 @@ package org.isoron.uhabits.models.sqlite; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import com.activeandroid.query.From; -import com.activeandroid.query.Select; -import com.activeandroid.query.Update; +import com.activeandroid.query.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.HabitList; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * Implementation of a {@link HabitList} that is backed by SQLite. @@ -42,11 +37,19 @@ public class SQLiteHabitList extends HabitList private HashMap cache; - public SQLiteHabitList() + private SQLiteHabitList() { cache = new HashMap<>(); } + /** + * Returns the global list of habits. + *

+ * There is only one list of habit per application, corresponding to the + * habits table of the SQLite database. + * + * @return the global list of habits. + */ public static SQLiteHabitList getInstance() { if (instance == null) instance = new SQLiteHabitList(); @@ -56,15 +59,15 @@ public class SQLiteHabitList extends HabitList @Override public void add(@NonNull Habit habit) { - if(cache.containsValue(habit)) - throw new RuntimeException("habit already in cache"); + if (cache.containsValue(habit)) + throw new IllegalArgumentException("habit already added"); HabitRecord record = new HabitRecord(); record.copyFrom(habit); record.position = countWithArchived(); Long id = habit.getId(); - if(id == null) id = record.save(); + if (id == null) id = record.save(); else record.save(id); habit.setId(id); @@ -72,7 +75,7 @@ public class SQLiteHabitList extends HabitList } @Override - public int count() + public int countActive() { return select().count(); } @@ -128,12 +131,14 @@ public class SQLiteHabitList extends HabitList .where("position = ?", position) .executeSingle(); - return getById(record.getId()); + if(record != null) return getById(record.getId()); + return null; } @Override public int indexOf(@NonNull Habit h) { + if (h.getId() == null) return -1; HabitRecord record = HabitRecord.get(h.getId()); if (record == null) return -1; return record.position; 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 ea468d32c..620e9816d 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 @@ -19,64 +19,66 @@ package org.isoron.uhabits.models.sqlite; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import com.activeandroid.query.Delete; -import com.activeandroid.query.From; -import com.activeandroid.query.Select; +import com.activeandroid.query.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Repetition; -import org.isoron.uhabits.models.RepetitionList; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; +import org.isoron.uhabits.utils.*; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * Implementation of a {@link RepetitionList} that is backed by SQLite. */ public class SQLiteRepetitionList extends RepetitionList { - HashMap cache; - public SQLiteRepetitionList(@NonNull Habit habit) { super(habit); - this.cache = new HashMap<>(); } + /** + * Adds a repetition to the global SQLite database. + *

+ * Given a repetition, this creates and saves the corresponding + * RepetitionRecord to the database. + * + * @param rep the repetition to be added + */ @Override public void add(Repetition rep) { RepetitionRecord record = new RepetitionRecord(); record.copyFrom(rep); - long id = record.save(); - cache.put(id, rep); + record.save(); observable.notifyListeners(); } @Override public List getByInterval(long timeFrom, long timeTo) { - return getFromRecord(selectFromTo(timeFrom, timeTo).execute()); + return toRepetitions(selectFromTo(timeFrom, timeTo).execute()); } @Override + @Nullable public Repetition getByTimestamp(long timestamp) { RepetitionRecord record = select().where("timestamp = ?", timestamp).executeSingle(); - return getFromRecord(record); + + if (record == null) return null; + return record.toRepetition(); } @Override public Repetition getOldest() { RepetitionRecord record = select().limit(1).executeSingle(); - return getFromRecord(record); + if (record == null) return null; + return record.toRepetition(); } @Override @@ -91,38 +93,6 @@ public class SQLiteRepetitionList extends RepetitionList observable.notifyListeners(); } - @NonNull - private List getFromRecord( - @Nullable List records) - { - List reps = new LinkedList<>(); - if (records == null) return reps; - - for (RepetitionRecord record : records) - { - Repetition rep = getFromRecord(record); - reps.add(rep); - } - - return reps; - } - - @Nullable - private Repetition getFromRecord(@Nullable RepetitionRecord record) - { - if (record == null) return null; - - Long id = record.getId(); - - if (!cache.containsKey(id)) - { - Repetition repetition = record.toRepetition(); - cache.put(id, repetition); - } - - return cache.get(id); - } - @NonNull private From select() { @@ -140,4 +110,17 @@ public class SQLiteRepetitionList extends RepetitionList .and("timestamp >= ?", timeFrom) .and("timestamp <= ?", timeTo); } + + @NonNull + private List toRepetitions( + @Nullable List records) + { + List reps = new LinkedList<>(); + if (records == null) return reps; + + for (RepetitionRecord record : records) + reps.add(record.toRepetition()); + + return reps; + } } diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteScoreList.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteScoreList.java index 63b68922c..309dd1865 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteScoreList.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLiteScoreList.java @@ -19,23 +19,16 @@ package org.isoron.uhabits.models.sqlite; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteStatement; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.database.sqlite.*; +import android.support.annotation.*; -import com.activeandroid.Cache; -import com.activeandroid.query.Delete; -import com.activeandroid.query.From; -import com.activeandroid.query.Select; -import com.activeandroid.util.SQLiteUtils; +import com.activeandroid.*; +import com.activeandroid.query.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Score; -import org.isoron.uhabits.models.ScoreList; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * Implementation of a ScoreList that is backed by SQLite. @@ -52,62 +45,33 @@ public class SQLiteScoreList extends ScoreList super(habit); } - @Override - public int getValue(long timestamp) - { - computeAll(); - String[] args = {habit.getId().toString(), Long.toString(timestamp)}; - return SQLiteUtils.intQuery( - "select score from Score where habit = ? and timestamp = ?", args); - } - - @Override - public void invalidateNewerThan(long timestamp) - { - new Delete() - .from(ScoreRecord.class) - .where("habit = ?", habit.getId()) - .and("timestamp >= ?", timestamp) - .execute(); - } - @Override @NonNull public List getAll() { + computeAll(); + List records = select().execute(); List scores = new LinkedList<>(); - for(ScoreRecord rec : records) + for (ScoreRecord rec : records) scores.add(rec.toScore()); return scores; } - @Nullable - @Override - protected Score getNewestComputed() - { - ScoreRecord record = select().limit(1).executeSingle(); - if(record == null) return null; - return record.toScore(); - } - @Override - @Nullable - protected Score get(long timestamp) + public void invalidateNewerThan(long timestamp) { - computeAll(); - - ScoreRecord record = - select().where("timestamp = ?", timestamp).executeSingle(); - - if(record == null) return null; - return record.toScore(); + new Delete() + .from(ScoreRecord.class) + .where("habit = ?", habit.getId()) + .and("timestamp >= ?", timestamp) + .execute(); } @Override - protected void add(List scores) + public void add(List scores) { String query = "insert into Score(habit, timestamp, score) values (?,?,?)"; @@ -135,7 +99,29 @@ public class SQLiteScoreList extends ScoreList } } - protected From select() + @Override + @Nullable + public Score getByTimestamp(long timestamp) + { + computeAll(); + + ScoreRecord record = + select().where("timestamp = ?", timestamp).executeSingle(); + + if (record == null) return null; + return record.toScore(); + } + + @Nullable + @Override + protected Score getNewestComputed() + { + ScoreRecord record = select().limit(1).executeSingle(); + if (record == null) return null; + return record.toScore(); + } + + private From select() { return new Select() .from(ScoreRecord.class) 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 410a1288e..322fd60e8 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 @@ -19,20 +19,15 @@ package org.isoron.uhabits.models.sqlite; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.annotation.*; -import com.activeandroid.query.Delete; -import com.activeandroid.query.Select; +import com.activeandroid.query.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Streak; -import org.isoron.uhabits.models.StreakList; -import org.isoron.uhabits.utils.DatabaseUtils; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.records.*; +import org.isoron.uhabits.utils.*; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * Implementation of a StreakList that is backed by SQLite. @@ -61,7 +56,7 @@ public class SQLiteStreakList extends StreakList public Streak getNewestComputed() { StreakRecord newestRecord = getNewestRecord(); - if(newestRecord == null) return null; + if (newestRecord == null) return null; return newestRecord.toStreak(); } @@ -77,19 +72,8 @@ public class SQLiteStreakList extends StreakList observable.notifyListeners(); } - @Nullable - private StreakRecord getNewestRecord() - { - return new Select() - .from(StreakRecord.class) - .where("habit = ?", habit.getId()) - .orderBy("end desc") - .limit(1) - .executeSingle(); - } - @Override - protected void insert(@NonNull List streaks) + protected void add(@NonNull List streaks) { DatabaseUtils.executeAsTransaction(() -> { for (Streak streak : streaks) @@ -101,6 +85,24 @@ public class SQLiteStreakList extends StreakList }); } + @Override + protected void removeNewestComputed() + { + StreakRecord newestStreak = getNewestRecord(); + if (newestStreak != null) newestStreak.delete(); + } + + @Nullable + private StreakRecord getNewestRecord() + { + return new Select() + .from(StreakRecord.class) + .where("habit = ?", habit.getId()) + .orderBy("end desc") + .limit(1) + .executeSingle(); + } + @NonNull private List recordsToStreaks(List records) { @@ -111,11 +113,4 @@ public class SQLiteStreakList extends StreakList return streaks; } - - @Override - protected void removeNewestComputed() - { - StreakRecord newestStreak = getNewestRecord(); - if (newestStreak != null) newestStreak.delete(); - } } diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/CheckmarkRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/CheckmarkRecord.java similarity index 87% rename from app/src/main/java/org/isoron/uhabits/models/sqlite/CheckmarkRecord.java rename to app/src/main/java/org/isoron/uhabits/models/sqlite/records/CheckmarkRecord.java index a35d7a1c9..f3512bd40 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/CheckmarkRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/CheckmarkRecord.java @@ -17,14 +17,13 @@ * with this program. If not, see . */ -package org.isoron.uhabits.models.sqlite; +package org.isoron.uhabits.models.sqlite.records; -import com.activeandroid.Model; -import com.activeandroid.annotation.Column; -import com.activeandroid.annotation.Table; +import com.activeandroid.*; +import com.activeandroid.annotation.*; -import org.isoron.uhabits.models.Checkmark; -import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.*; /** * The SQLite database record corresponding to a {@link Checkmark}. diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/HabitRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/HabitRecord.java similarity index 89% rename from app/src/main/java/org/isoron/uhabits/models/sqlite/HabitRecord.java rename to app/src/main/java/org/isoron/uhabits/models/sqlite/records/HabitRecord.java index 052739ef5..e4c2e04d2 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/HabitRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/HabitRecord.java @@ -17,20 +17,18 @@ * with this program. If not, see . */ -package org.isoron.uhabits.models.sqlite; +package org.isoron.uhabits.models.sqlite.records; -import android.annotation.SuppressLint; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.annotation.*; +import android.support.annotation.*; -import com.activeandroid.Model; -import com.activeandroid.annotation.Column; -import com.activeandroid.annotation.Table; -import com.activeandroid.query.Delete; -import com.activeandroid.util.SQLiteUtils; +import com.activeandroid.*; +import com.activeandroid.annotation.*; +import com.activeandroid.query.*; +import com.activeandroid.util.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.utils.DatabaseUtils; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.utils.*; /** * The SQLite database record corresponding to a {@link Habit}. @@ -83,7 +81,7 @@ public class HabitRecord extends Model } @Nullable - public static HabitRecord get(Long id) + public static HabitRecord get(long id) { return HabitRecord.load(HabitRecord.class, id); } diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/RepetitionRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java similarity index 86% rename from app/src/main/java/org/isoron/uhabits/models/sqlite/RepetitionRecord.java rename to app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java index e29f792de..ffc524701 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/RepetitionRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/RepetitionRecord.java @@ -17,14 +17,13 @@ * with this program. If not, see . */ -package org.isoron.uhabits.models.sqlite; +package org.isoron.uhabits.models.sqlite.records; -import com.activeandroid.Model; -import com.activeandroid.annotation.Column; -import com.activeandroid.annotation.Table; +import com.activeandroid.*; +import com.activeandroid.annotation.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Repetition; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.*; /** * The SQLite database record corresponding to a {@link Repetition}. diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/ScoreRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/ScoreRecord.java similarity index 87% rename from app/src/main/java/org/isoron/uhabits/models/sqlite/ScoreRecord.java rename to app/src/main/java/org/isoron/uhabits/models/sqlite/records/ScoreRecord.java index 2efc2e21e..2fd2b91b0 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/ScoreRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/ScoreRecord.java @@ -17,14 +17,13 @@ * with this program. If not, see . */ -package org.isoron.uhabits.models.sqlite; +package org.isoron.uhabits.models.sqlite.records; -import com.activeandroid.Model; -import com.activeandroid.annotation.Column; -import com.activeandroid.annotation.Table; +import com.activeandroid.*; +import com.activeandroid.annotation.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Score; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.*; /** * The SQLite database record corresponding to a Score. diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/StreakRecord.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/StreakRecord.java similarity index 87% rename from app/src/main/java/org/isoron/uhabits/models/sqlite/StreakRecord.java rename to app/src/main/java/org/isoron/uhabits/models/sqlite/records/StreakRecord.java index fd4229073..c1b4b0a56 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/StreakRecord.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/records/StreakRecord.java @@ -17,14 +17,13 @@ * with this program. If not, see . */ -package org.isoron.uhabits.models.sqlite; +package org.isoron.uhabits.models.sqlite.records; -import com.activeandroid.Model; -import com.activeandroid.annotation.Column; -import com.activeandroid.annotation.Table; +import com.activeandroid.*; +import com.activeandroid.annotation.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Streak; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.models.sqlite.*; /** * The SQLite database record corresponding to a Streak. 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 377edb83a..7215430ee 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 @@ -51,10 +51,10 @@ import java.util.Random; public class HabitScoreView extends ScrollableDataView implements HabitDataView, ModelObservable.Listener { - public static final PorterDuffXfermode XFERMODE_CLEAR = + private static final PorterDuffXfermode XFERMODE_CLEAR = new PorterDuffXfermode(PorterDuff.Mode.CLEAR); - public static final PorterDuffXfermode XFERMODE_SRC = + private static final PorterDuffXfermode XFERMODE_SRC = new PorterDuffXfermode(PorterDuff.Mode.SRC); public static int DEFAULT_BUCKET_SIZES[] = {1, 7, 31, 92, 365}; diff --git a/app/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java b/app/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java index 5381590f8..4ef5ae48f 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java +++ b/app/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java @@ -19,25 +19,17 @@ package org.isoron.uhabits.utils; -import android.content.Context; -import android.database.Cursor; -import android.support.annotation.NonNull; - -import com.activeandroid.ActiveAndroid; -import com.activeandroid.Cache; -import com.activeandroid.Configuration; - -import org.isoron.uhabits.BuildConfig; -import org.isoron.uhabits.HabitsApplication; -import org.isoron.uhabits.models.sqlite.CheckmarkRecord; -import org.isoron.uhabits.models.sqlite.HabitRecord; -import org.isoron.uhabits.models.sqlite.RepetitionRecord; -import org.isoron.uhabits.models.sqlite.ScoreRecord; -import org.isoron.uhabits.models.sqlite.StreakRecord; - -import java.io.File; -import java.io.IOException; -import java.text.SimpleDateFormat; +import android.content.*; +import android.database.*; +import android.support.annotation.*; + +import com.activeandroid.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.sqlite.records.*; + +import java.io.*; +import java.text.*; public abstract class DatabaseUtils { diff --git a/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java index 4447ac2c9..e37f59bf1 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java @@ -18,15 +18,14 @@ */ package org.isoron.uhabits.widgets; -import android.app.PendingIntent; -import android.content.Context; -import android.view.View; +import android.app.*; +import android.content.*; +import android.view.*; -import org.isoron.uhabits.HabitBroadcastReceiver; -import org.isoron.uhabits.R; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.widgets.views.CheckmarkWidgetView; -import org.isoron.uhabits.ui.habits.show.views.HabitDataView; +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.habits.show.views.*; +import org.isoron.uhabits.widgets.views.*; public class CheckmarkWidgetProvider extends BaseWidgetProvider { @@ -39,33 +38,34 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider } @Override - protected void refreshCustomViewData(View view) + protected int getDefaultHeight() { - ((HabitDataView) view).refreshData(); + return 125; } @Override - protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) + protected int getDefaultWidth() { - return HabitBroadcastReceiver.buildCheckIntent(context, habit, null); + return 125; } @Override - protected int getDefaultHeight() + protected int getLayoutId() { - return 125; + return R.layout.widget_wrapper; } @Override - protected int getDefaultWidth() + protected PendingIntent getOnClickPendingIntent(Context context, + Habit habit) { - return 125; + return HabitBroadcastReceiver.buildCheckIntent(context, habit, null); } @Override - protected int getLayoutId() + protected void refreshCustomViewData(View view) { - return R.layout.widget_wrapper; + ((HabitDataView) view).refreshData(); } diff --git a/app/src/test/java/org/isoron/uhabits/models/CheckmarkListTest.java b/app/src/test/java/org/isoron/uhabits/models/CheckmarkListTest.java index 02757d335..502e4fe45 100644 --- a/app/src/test/java/org/isoron/uhabits/models/CheckmarkListTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/CheckmarkListTest.java @@ -20,6 +20,7 @@ package org.isoron.uhabits.models; import org.isoron.uhabits.BaseUnitTest; +import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.utils.DateUtils; import org.junit.Test; 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 a79ad5959..698b67322 100644 --- a/app/src/test/java/org/isoron/uhabits/models/HabitListTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/HabitListTest.java @@ -19,20 +19,17 @@ package org.isoron.uhabits.models; -import org.hamcrest.MatcherAssert; -import org.isoron.uhabits.BaseUnitTest; -import org.isoron.uhabits.utils.DateUtils; -import org.junit.Test; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; - -import static junit.framework.Assert.fail; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; +import org.hamcrest.*; +import org.isoron.uhabits.*; +import org.isoron.uhabits.utils.*; +import org.junit.*; + +import java.io.*; +import java.util.*; + +import static junit.framework.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.core.IsEqual.equalTo; public class HabitListTest extends BaseUnitTest @@ -73,7 +70,7 @@ public class HabitListTest extends BaseUnitTest @Test public void test_count() { - assertThat(list.count(), equalTo(6)); + assertThat(list.countActive(), equalTo(6)); } @Test diff --git a/app/src/test/java/org/isoron/uhabits/models/RepetitionListTest.java b/app/src/test/java/org/isoron/uhabits/models/RepetitionListTest.java index 51dae4c14..425a05bca 100644 --- a/app/src/test/java/org/isoron/uhabits/models/RepetitionListTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/RepetitionListTest.java @@ -22,6 +22,7 @@ package org.isoron.uhabits.models; import android.support.annotation.NonNull; import org.isoron.uhabits.BaseUnitTest; +import org.isoron.uhabits.models.*; import org.isoron.uhabits.utils.DateUtils; import org.junit.After; import org.junit.Before; @@ -33,7 +34,7 @@ import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Random; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.Mockito.mock; 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 9fdc2d48a..8b1007575 100644 --- a/app/src/test/java/org/isoron/uhabits/models/ScoreListTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/ScoreListTest.java @@ -76,7 +76,7 @@ public class ScoreListTest extends BaseUnitTest int actualValues[] = new int[expectedValues.length]; int i = 0; - for (Score s : habit.getScores().getAll()) + for (Score s : habit.getScores()) actualValues[i++] = s.getValue(); assertThat(actualValues, equalTo(expectedValues)); 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 3107b6a1b..a4253d543 100644 --- a/app/src/test/java/org/isoron/uhabits/models/StreakListTest.java +++ b/app/src/test/java/org/isoron/uhabits/models/StreakListTest.java @@ -19,16 +19,15 @@ package org.isoron.uhabits.models; -import org.isoron.uhabits.BaseUnitTest; -import org.isoron.uhabits.utils.DateUtils; -import org.junit.Test; +import org.isoron.uhabits.*; +import org.isoron.uhabits.utils.*; +import org.junit.*; -import java.util.List; +import java.util.*; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; public class StreakListTest extends BaseUnitTest {