Add instrumented unit tests for SQLite lists

pull/145/head
Alinson S. Xavier 9 years ago
parent 2b23b36e36
commit 9a44774284

@ -15,6 +15,7 @@ android {
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArgument "size", "medium"
}
buildTypes {

@ -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()

@ -0,0 +1,117 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.models.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<Checkmark> 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<CheckmarkRecord> 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<Checkmark> 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<CheckmarkRecord> 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<CheckmarkRecord> getAllRecords()
{
return new Select()
.from(CheckmarkRecord.class)
.where("habit = ?", habit.getId())
.orderBy("timestamp desc")
.execute();
}
}

@ -0,0 +1,229 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.models.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<Habit> habits = habitList.getAll(true);
assertThat(habits.size(), equalTo(10));
assertThat(habits.get(3).getName(), equalTo("habit 3"));
}
@Test
public void testGetAll_withoutArchived()
{
List<Habit> habits = habitList.getAll(false);
assertThat(habits.size(), equalTo(5));
assertThat(habits.get(3).getName(), equalTo("habit 7"));
List<Habit> 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();
}
}

@ -0,0 +1,145 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.models.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<Repetition> 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);
}
}

@ -0,0 +1,126 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.models.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<Score> 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<ScoreRecord> 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<Score> 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<ScoreRecord> 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<ScoreRecord> getAllRecords()
{
return new Select()
.from(ScoreRecord.class)
.where("habit = ?", habit.getId())
.orderBy("timestamp desc")
.execute();
}
}

@ -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;

@ -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;

@ -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));
}

@ -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);
}

@ -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

@ -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);

@ -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);

@ -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);

@ -47,7 +47,7 @@ public class UnarchiveHabitsCommandTest extends BaseAndroidTest
{
super.setUp();
habit = habitFixtures.createShortHabit();
habit = fixtures.createShortHabit();
habit.setArchived(1);
habitList.update(habit);

@ -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();

@ -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");

@ -1,33 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.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
{
}

@ -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<Habit> habits = habitList.getAll(true);
ExportCSVTask task = new ExportCSVTask(habits, null);

@ -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);

@ -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);

@ -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);

@ -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);

@ -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);

@ -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;

@ -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.
* <p>
* 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<Checkmark> 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.
* <p>
* 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.
* <p>
* 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<Checkmark> 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<Checkmark> 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<Checkmark> 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.
* <p>
* 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();
}

@ -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;
}

@ -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.
* <p>
* 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.
* <p>
* 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.

@ -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);

@ -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

@ -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

@ -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.
* <p>
* 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<Repetition> getByInterval(long fromTimestamp,
long toTimestamp);

@ -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;

@ -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<Score>
{
protected final Habit habit;
@ -53,8 +47,29 @@ public abstract class ScoreList
observable = new ModelObservable();
}
/**
* Adds the given scores to the list.
* <p>
* 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<Score> scores);
public abstract List<Score> getAll();
/**
* Returns the score that has the given timestamp.
* <p>
* 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.
* <p>
* 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<Score> groupBy(DateUtils.TruncateField field)
{
@ -95,12 +119,18 @@ public abstract class ScoreList
*/
public abstract void invalidateNewerThan(long timestamp);
@Override
public Iterator<Score> 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<Score> 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.
* <p>
* 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<Long, ArrayList<Long>> getGroupedValues(DateUtils.TruncateField field)
{
HashMap<Long, ArrayList<Long>> 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.
* <p>
* 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<Score> groupsToAvgScores(HashMap<Long, ArrayList<Long>> groups)
{

@ -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
{

@ -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<Streak> 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<Streak> streaks);
protected abstract void add(@NonNull List<Streak> streaks);
protected abstract void removeNewestComputed();
}

@ -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<Checkmark> checkmarks)
{
list.addAll(checkmarks);
Collections.sort(list, (c1, c2) -> c2.compareNewer(c1));
}
@NonNull
@Override
public List<Checkmark> 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<Checkmark> 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()));
}
}

@ -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<Habit> 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);
}

@ -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);
}

@ -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<Repetition> getByInterval(long fromTimestamp, long toTimestamp)
{
LinkedList<Repetition> filtered = new LinkedList<>();
for (Repetition r : list)
{
long t = r.getTimestamp();

@ -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<Score> 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,6 +65,14 @@ public class MemoryScoreList extends ScoreList
return null;
}
@Override
public void add(List<Score> scores)
{
list.addAll(scores);
Collections.sort(list,
(s1, s2) -> Long.signum(s2.getTimestamp() - s1.getTimestamp()));
}
@Nullable
@Override
protected Score getNewestComputed()

@ -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
{
@ -44,8 +40,7 @@ public class MemoryStreakList extends StreakList
Streak newest = null;
for (Streak s : list)
if(newest == null || s.getEnd() > newest.getEnd())
newest = s;
if (newest == null || s.getEnd() > newest.getEnd()) newest = s;
return newest;
}
@ -64,7 +59,7 @@ public class MemoryStreakList extends StreakList
}
@Override
protected void insert(List<Streak> streaks)
protected void add(List<Streak> streaks)
{
list.addAll(streaks);
Collections.sort(list, (s1, s2) -> s2.compareNewer(s1));

@ -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

@ -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.
@ -45,96 +42,82 @@ public class SQLiteCheckmarkList extends CheckmarkList
}
@Override
public void invalidateNewerThan(long timestamp)
public void add(List<Checkmark> checkmarks)
{
new Delete()
.from(CheckmarkRecord.class)
.where("habit = ?", habit.getId())
.and("timestamp >= ?", timestamp)
.execute();
observable.notifyListeners();
}
String query =
"insert into Checkmarks(habit, timestamp, value) values (?,?,?)";
@Override
@NonNull
public int[] getValues(long fromTimestamp, long toTimestamp)
SQLiteDatabase db = Cache.openDatabase();
db.beginTransaction();
try
{
compute(fromTimestamp, toTimestamp);
if (fromTimestamp > toTimestamp) return new int[0];
SQLiteStatement statement = db.compileStatement(query);
String query = "select value, timestamp from Checkmarks where " +
"habit = ? and timestamp >= ? and timestamp <= ?";
for (Checkmark c : checkmarks)
{
statement.bindLong(1, habit.getId());
statement.bindLong(2, c.getTimestamp());
statement.bindLong(3, c.getValue());
statement.execute();
}
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())
db.setTransactionSuccessful();
}
finally
{
do
db.endTransaction();
}
}
@Override
public List<Checkmark> getByInterval(long fromTimestamp, long toTimestamp)
{
long timestamp = cursor.getLong(1);
int offset = (int) ((timestamp - fromTimestamp) / day);
checks[nDays - offset - 1] = cursor.getInt(0);
computeAll();
} while (cursor.moveToNext());
}
List<CheckmarkRecord> records = select()
.and("timestamp >= ?", fromTimestamp)
.and("timestamp <= ?", toTimestamp)
.execute();
cursor.close();
return checks;
return toCheckmarks(records);
}
@Override
@Nullable
protected Checkmark getNewest()
public void invalidateNewerThan(long timestamp)
{
CheckmarkRecord record = new Select()
new Delete()
.from(CheckmarkRecord.class)
.where("habit = ?", habit.getId())
.and("timestamp <= ?", DateUtils.getStartOfToday())
.orderBy("timestamp desc")
.limit(1)
.executeSingle();
.and("timestamp >= ?", timestamp)
.execute();
if(record == null) return null;
return record.toCheckmark();
observable.notifyListeners();
}
@Override
protected void insert(long timestamps[], int values[])
{
String query =
"insert into Checkmarks(habit, timestamp, value) values (?,?,?)";
SQLiteDatabase db = Cache.openDatabase();
db.beginTransaction();
try
@Nullable
protected Checkmark getNewestComputed()
{
SQLiteStatement statement = db.compileStatement(query);
CheckmarkRecord record = select().limit(1).executeSingle();
if (record == null) return null;
return record.toCheckmark();
}
for (int i = 0; i < timestamps.length; i++)
@NonNull
private From select()
{
statement.bindLong(1, habit.getId());
statement.bindLong(2, timestamps[i]);
statement.bindLong(3, values[i]);
statement.execute();
return new Select()
.from(CheckmarkRecord.class)
.where("habit = ?", habit.getId())
.and("timestamp <= ?", DateUtils.getStartOfToday())
.orderBy("timestamp desc");
}
db.setTransactionSuccessful();
}
finally
@NonNull
private List<Checkmark> toCheckmarks(@NonNull List<CheckmarkRecord> records)
{
db.endTransaction();
}
List<Checkmark> checkmarks = new LinkedList<>();
for (CheckmarkRecord r : records) checkmarks.add(r.toCheckmark());
return checkmarks;
}
}

@ -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<Long, Habit> cache;
public SQLiteHabitList()
private SQLiteHabitList()
{
cache = new HashMap<>();
}
/**
* Returns the global list of habits.
* <p>
* 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();
@ -57,7 +60,7 @@ public class SQLiteHabitList extends HabitList
public void add(@NonNull Habit habit)
{
if (cache.containsValue(habit))
throw new RuntimeException("habit already in cache");
throw new IllegalArgumentException("habit already added");
HabitRecord record = new HabitRecord();
record.copyFrom(habit);
@ -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;

@ -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<Long, Repetition> cache;
public SQLiteRepetitionList(@NonNull Habit habit)
{
super(habit);
this.cache = new HashMap<>();
}
/**
* Adds a repetition to the global SQLite database.
* <p>
* 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<Repetition> 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<Repetition> getFromRecord(
@Nullable List<RepetitionRecord> records)
{
List<Repetition> 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<Repetition> toRepetitions(
@Nullable List<RepetitionRecord> records)
{
List<Repetition> reps = new LinkedList<>();
if (records == null) return reps;
for (RepetitionRecord record : records)
reps.add(record.toRepetition());
return reps;
}
}

@ -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,29 +45,12 @@ 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<Score> getAll()
{
computeAll();
List<ScoreRecord> records = select().execute();
List<Score> scores = new LinkedList<>();
@ -84,30 +60,18 @@ public class SQLiteScoreList extends ScoreList
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<Score> scores)
public void add(List<Score> 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)

@ -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.
@ -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<Streak> streaks)
protected void add(@NonNull List<Streak> 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<Streak> recordsToStreaks(List<StreakRecord> records)
{
@ -111,11 +113,4 @@ public class SQLiteStreakList extends StreakList
return streaks;
}
@Override
protected void removeNewestComputed()
{
StreakRecord newestStreak = getNewestRecord();
if (newestStreak != null) newestStreak.delete();
}
}

@ -17,14 +17,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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}.

@ -17,20 +17,18 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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);
}

@ -17,14 +17,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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}.

@ -17,14 +17,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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.

@ -17,14 +17,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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.

@ -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};

@ -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
{

@ -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();
}

@ -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;

@ -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

@ -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;

@ -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));

@ -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
{

Loading…
Cancel
Save