mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Simplify SQLite lists
This commit is contained in:
@@ -115,9 +115,8 @@ public class HabitFixtures
|
|||||||
return habit;
|
return habit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void purgeHabits(HabitList habitList)
|
public synchronized void purgeHabits(HabitList habitList)
|
||||||
{
|
{
|
||||||
for (Habit h : habitList)
|
habitList.removeAll();
|
||||||
habitList.remove(h);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ package org.isoron.uhabits.models.sqlite;
|
|||||||
import android.support.test.runner.*;
|
import android.support.test.runner.*;
|
||||||
import android.test.suitebuilder.annotation.*;
|
import android.test.suitebuilder.annotation.*;
|
||||||
|
|
||||||
|
import com.activeandroid.*;
|
||||||
|
|
||||||
|
import org.isoron.androidbase.storage.*;
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
import org.isoron.uhabits.models.sqlite.records.*;
|
import org.isoron.uhabits.models.sqlite.records.*;
|
||||||
@@ -35,25 +38,18 @@ import static org.hamcrest.core.IsEqual.*;
|
|||||||
@MediumTest
|
@MediumTest
|
||||||
public class HabitRecordTest extends BaseAndroidTest
|
public class HabitRecordTest extends BaseAndroidTest
|
||||||
{
|
{
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
private SQLiteRepository<HabitRecord> sqlite =
|
||||||
|
new SQLiteRepository<>(HabitRecord.class, Cache.openDatabase());
|
||||||
|
|
||||||
|
@Before
|
||||||
@Override
|
@Override
|
||||||
public void setUp()
|
public void setUp()
|
||||||
{
|
{
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
|
||||||
Habit h = component.getModelFactory().buildHabit();
|
habit = component.getModelFactory().buildHabit();
|
||||||
h.setName("Hello world");
|
|
||||||
h.setId(1000L);
|
|
||||||
|
|
||||||
HabitRecord record = new HabitRecord();
|
|
||||||
record.copyFrom(h);
|
|
||||||
record.position = 0;
|
|
||||||
record.save(1000L);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCopyFrom()
|
|
||||||
{
|
|
||||||
Habit habit = component.getModelFactory().buildHabit();
|
|
||||||
habit.setName("Hello world");
|
habit.setName("Hello world");
|
||||||
habit.setDescription("Did you greet the world today?");
|
habit.setDescription("Did you greet the world today?");
|
||||||
habit.setColor(1);
|
habit.setColor(1);
|
||||||
@@ -61,7 +57,11 @@ public class HabitRecordTest extends BaseAndroidTest
|
|||||||
habit.setFrequency(Frequency.THREE_TIMES_PER_WEEK);
|
habit.setFrequency(Frequency.THREE_TIMES_PER_WEEK);
|
||||||
habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
|
habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
|
||||||
habit.setId(1000L);
|
habit.setId(1000L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCopyFrom()
|
||||||
|
{
|
||||||
HabitRecord rec = new HabitRecord();
|
HabitRecord rec = new HabitRecord();
|
||||||
rec.copyFrom(habit);
|
rec.copyFrom(habit);
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,12 @@ package org.isoron.uhabits.models.sqlite;
|
|||||||
import android.support.test.runner.*;
|
import android.support.test.runner.*;
|
||||||
import android.test.suitebuilder.annotation.*;
|
import android.test.suitebuilder.annotation.*;
|
||||||
|
|
||||||
import com.activeandroid.query.*;
|
import com.activeandroid.*;
|
||||||
|
import com.google.common.collect.*;
|
||||||
|
|
||||||
|
import org.isoron.androidbase.storage.*;
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
import org.isoron.uhabits.models.sqlite.*;
|
|
||||||
import org.isoron.uhabits.models.sqlite.records.*;
|
import org.isoron.uhabits.models.sqlite.records.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.junit.rules.*;
|
import org.junit.rules.*;
|
||||||
@@ -49,6 +50,8 @@ public class SQLiteHabitListTest extends BaseAndroidTest
|
|||||||
|
|
||||||
private ModelFactory modelFactory;
|
private ModelFactory modelFactory;
|
||||||
|
|
||||||
|
private SQLiteRepository<HabitRecord> repository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUp()
|
public void setUp()
|
||||||
{
|
{
|
||||||
@@ -57,6 +60,8 @@ public class SQLiteHabitListTest extends BaseAndroidTest
|
|||||||
fixtures.purgeHabits(habitList);
|
fixtures.purgeHabits(habitList);
|
||||||
|
|
||||||
modelFactory = component.getModelFactory();
|
modelFactory = component.getModelFactory();
|
||||||
|
repository =
|
||||||
|
new SQLiteRepository<>(HabitRecord.class, Cache.openDatabase());
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
@@ -68,8 +73,10 @@ public class SQLiteHabitListTest extends BaseAndroidTest
|
|||||||
HabitRecord record = new HabitRecord();
|
HabitRecord record = new HabitRecord();
|
||||||
record.copyFrom(h);
|
record.copyFrom(h);
|
||||||
record.position = i;
|
record.position = i;
|
||||||
record.save(i);
|
repository.save(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
habitList.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -91,7 +98,7 @@ public class SQLiteHabitListTest extends BaseAndroidTest
|
|||||||
habitList.add(habit);
|
habitList.add(habit);
|
||||||
assertThat(habit.getId(), equalTo(12300L));
|
assertThat(habit.getId(), equalTo(12300L));
|
||||||
|
|
||||||
HabitRecord record = getRecord(12300L);
|
HabitRecord record = repository.find(12300L);
|
||||||
assertNotNull(record);
|
assertNotNull(record);
|
||||||
assertThat(record.name, equalTo(habit.getName()));
|
assertThat(record.name, equalTo(habit.getName()));
|
||||||
}
|
}
|
||||||
@@ -106,7 +113,7 @@ public class SQLiteHabitListTest extends BaseAndroidTest
|
|||||||
habitList.add(habit);
|
habitList.add(habit);
|
||||||
assertNotNull(habit.getId());
|
assertNotNull(habit.getId());
|
||||||
|
|
||||||
HabitRecord record = getRecord(habit.getId());
|
HabitRecord record = repository.find(habit.getId());
|
||||||
assertNotNull(record);
|
assertNotNull(record);
|
||||||
assertThat(record.name, equalTo(habit.getName()));
|
assertThat(record.name, equalTo(habit.getName()));
|
||||||
}
|
}
|
||||||
@@ -120,7 +127,7 @@ public class SQLiteHabitListTest extends BaseAndroidTest
|
|||||||
@Test
|
@Test
|
||||||
public void testGetAll_withArchived()
|
public void testGetAll_withArchived()
|
||||||
{
|
{
|
||||||
List<Habit> habits = habitList.toList();
|
List<Habit> habits = Lists.newArrayList(habitList.iterator());
|
||||||
assertThat(habits.size(), equalTo(10));
|
assertThat(habits.size(), equalTo(10));
|
||||||
assertThat(habits.get(3).getName(), equalTo("habit 3"));
|
assertThat(habits.get(3).getName(), equalTo("habit 3"));
|
||||||
}
|
}
|
||||||
@@ -166,12 +173,4 @@ public class SQLiteHabitListTest extends BaseAndroidTest
|
|||||||
h2.setId(1000L);
|
h2.setId(1000L);
|
||||||
assertThat(habitList.indexOf(h2), equalTo(-1));
|
assertThat(habitList.indexOf(h2), equalTo(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private HabitRecord getRecord(long id)
|
|
||||||
{
|
|
||||||
return new Select()
|
|
||||||
.from(HabitRecord.class)
|
|
||||||
.where("id = ?", id)
|
|
||||||
.executeSingle();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -23,8 +23,9 @@ import android.support.annotation.*;
|
|||||||
import android.support.test.runner.*;
|
import android.support.test.runner.*;
|
||||||
import android.test.suitebuilder.annotation.*;
|
import android.test.suitebuilder.annotation.*;
|
||||||
|
|
||||||
import com.activeandroid.query.*;
|
import com.activeandroid.*;
|
||||||
|
|
||||||
|
import org.isoron.androidbase.storage.*;
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
import org.isoron.uhabits.core.utils.*;
|
import org.isoron.uhabits.core.utils.*;
|
||||||
@@ -35,7 +36,7 @@ import org.junit.runner.*;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
|
import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
|
||||||
import static org.hamcrest.core.IsEqual.equalTo;
|
import static org.hamcrest.core.IsEqual.*;
|
||||||
import static org.isoron.uhabits.core.models.Checkmark.CHECKED_EXPLICITLY;
|
import static org.isoron.uhabits.core.models.Checkmark.CHECKED_EXPLICITLY;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@@ -50,6 +51,8 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest
|
|||||||
|
|
||||||
private long day;
|
private long day;
|
||||||
|
|
||||||
|
private SQLiteRepository<RepetitionRecord> sqlite;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUp()
|
public void setUp()
|
||||||
{
|
{
|
||||||
@@ -59,6 +62,8 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest
|
|||||||
repetitions = habit.getRepetitions();
|
repetitions = habit.getRepetitions();
|
||||||
today = DateUtils.getStartOfToday();
|
today = DateUtils.getStartOfToday();
|
||||||
day = DateUtils.millisecondsInOneDay;
|
day = DateUtils.millisecondsInOneDay;
|
||||||
|
sqlite = new SQLiteRepository<>(RepetitionRecord.class,
|
||||||
|
Cache.openDatabase());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -130,15 +135,13 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest
|
|||||||
@Nullable
|
@Nullable
|
||||||
private RepetitionRecord getByTimestamp(long timestamp)
|
private RepetitionRecord getByTimestamp(long timestamp)
|
||||||
{
|
{
|
||||||
return selectByTimestamp(timestamp).executeSingle();
|
String query = "where habit = ? and timestamp = ?";
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
String params[] = {
|
||||||
private From selectByTimestamp(long timestamp)
|
Long.toString(habit.getId()),
|
||||||
{
|
Long.toString(timestamp)
|
||||||
return new Select()
|
};
|
||||||
.from(RepetitionRecord.class)
|
|
||||||
.where("habit = ?", habit.getId())
|
return sqlite.findFirst(query, params);
|
||||||
.and("timestamp = ?", timestamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ public class SQLModelFactory implements ModelFactory
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@AppScope
|
@AppScope
|
||||||
public static HabitList provideHabitList()
|
public static HabitList provideHabitList(ModelFactory modelFactory)
|
||||||
{
|
{
|
||||||
return SQLiteHabitList.getInstance(provideModelFactory());
|
return new SQLiteHabitList(modelFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,55 +19,61 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.models.sqlite;
|
package org.isoron.uhabits.models.sqlite;
|
||||||
|
|
||||||
|
import android.database.sqlite.*;
|
||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import com.activeandroid.query.*;
|
import com.activeandroid.*;
|
||||||
import com.activeandroid.util.*;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.*;
|
import org.isoron.androidbase.storage.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.isoron.uhabits.core.models.memory.*;
|
||||||
import org.isoron.uhabits.models.sqlite.records.*;
|
import org.isoron.uhabits.models.sqlite.records.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.utils.DatabaseUtils.executeAsTransaction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of a {@link HabitList} that is backed by SQLite.
|
* Implementation of a {@link HabitList} that is backed by SQLite.
|
||||||
*/
|
*/
|
||||||
public class SQLiteHabitList extends HabitList
|
public class SQLiteHabitList extends HabitList
|
||||||
{
|
{
|
||||||
private static HashMap<Long, Habit> cache;
|
|
||||||
|
|
||||||
private static SQLiteHabitList instance;
|
private static SQLiteHabitList instance;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final SQLiteUtils<HabitRecord> sqlite;
|
private final SQLiteRepository<HabitRecord> repository;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final ModelFactory modelFactory;
|
private final ModelFactory modelFactory;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private Order order;
|
private final MemoryHabitList list;
|
||||||
|
|
||||||
|
private boolean loaded = false;
|
||||||
|
|
||||||
public SQLiteHabitList(@NonNull ModelFactory modelFactory)
|
public SQLiteHabitList(@NonNull ModelFactory modelFactory)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
this.modelFactory = modelFactory;
|
this.modelFactory = modelFactory;
|
||||||
|
this.list = new MemoryHabitList();
|
||||||
|
|
||||||
if (cache == null) cache = new HashMap<>();
|
repository =
|
||||||
sqlite = new SQLiteUtils<>(HabitRecord.class);
|
new SQLiteRepository<>(HabitRecord.class, Cache.openDatabase());
|
||||||
order = Order.BY_POSITION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SQLiteHabitList(@NonNull ModelFactory modelFactory,
|
private void loadRecords()
|
||||||
@NonNull HabitMatcher filter,
|
|
||||||
@NonNull Order order)
|
|
||||||
{
|
{
|
||||||
super(filter);
|
if(loaded) return;
|
||||||
this.modelFactory = modelFactory;
|
loaded = true;
|
||||||
|
|
||||||
if (cache == null) cache = new HashMap<>();
|
list.removeAll();
|
||||||
sqlite = new SQLiteUtils<>(HabitRecord.class);
|
List<HabitRecord> records = repository.findAll("order by position");
|
||||||
this.order = order;
|
for (HabitRecord rec : records)
|
||||||
|
{
|
||||||
|
Habit h = modelFactory.buildHabit();
|
||||||
|
rec.copyTo(h);
|
||||||
|
list.add(h);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SQLiteHabitList getInstance(
|
public static SQLiteHabitList getInstance(
|
||||||
@@ -78,127 +84,120 @@ public class SQLiteHabitList extends HabitList
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(@NonNull Habit habit)
|
public synchronized void add(@NonNull Habit habit)
|
||||||
{
|
{
|
||||||
if (cache.containsValue(habit))
|
loadRecords();
|
||||||
throw new IllegalArgumentException("habit already added");
|
list.add(habit);
|
||||||
|
|
||||||
HabitRecord record = new HabitRecord();
|
HabitRecord record = new HabitRecord();
|
||||||
record.copyFrom(habit);
|
record.copyFrom(habit);
|
||||||
record.position = size();
|
record.position = list.indexOf(habit);
|
||||||
|
repository.save(record);
|
||||||
Long id = habit.getId();
|
|
||||||
if (id == null) id = record.save();
|
|
||||||
else record.save(id);
|
|
||||||
|
|
||||||
if (id < 0)
|
|
||||||
throw new IllegalArgumentException("habit could not be saved");
|
|
||||||
|
|
||||||
habit.setId(id);
|
|
||||||
cache.put(id, habit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Habit getById(long id)
|
public Habit getById(long id)
|
||||||
{
|
{
|
||||||
if (!cache.containsKey(id))
|
loadRecords();
|
||||||
{
|
return list.getById(id);
|
||||||
HabitRecord record = HabitRecord.get(id);
|
|
||||||
if (record == null) return null;
|
|
||||||
|
|
||||||
Habit habit = modelFactory.buildHabit();
|
|
||||||
record.copyTo(habit);
|
|
||||||
cache.put(id, habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cache.get(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Habit getByPosition(int position)
|
public Habit getByPosition(int position)
|
||||||
{
|
{
|
||||||
return toList().get(position);
|
loadRecords();
|
||||||
|
return list.getByPosition(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public HabitList getFiltered(HabitMatcher filter)
|
public HabitList getFiltered(HabitMatcher filter)
|
||||||
{
|
{
|
||||||
return new SQLiteHabitList(modelFactory, filter, order);
|
loadRecords();
|
||||||
|
return list.getFiltered(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Order getOrder()
|
public Order getOrder()
|
||||||
{
|
{
|
||||||
return order;
|
return list.getOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOrder(@NonNull Order order)
|
public void setOrder(@NonNull Order order)
|
||||||
{
|
{
|
||||||
this.order = order;
|
list.setOrder(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int indexOf(@NonNull Habit h)
|
public int indexOf(@NonNull Habit h)
|
||||||
{
|
{
|
||||||
return toList().indexOf(h);
|
loadRecords();
|
||||||
|
return list.indexOf(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Habit> iterator()
|
public Iterator<Habit> iterator()
|
||||||
{
|
{
|
||||||
return Collections.unmodifiableCollection(toList()).iterator();
|
loadRecords();
|
||||||
|
return list.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void rebuildOrder()
|
private void rebuildOrder()
|
||||||
{
|
{
|
||||||
List<Habit> habits = toList();
|
// List<Habit> habits = toList();
|
||||||
|
//
|
||||||
int i = 0;
|
// int i = 0;
|
||||||
for (Habit h : habits)
|
// for (Habit h : habits)
|
||||||
{
|
// {
|
||||||
HabitRecord record = HabitRecord.get(h.getId());
|
// HabitRecord record = repository.find(h.getId());
|
||||||
if (record == null)
|
// if (record == null)
|
||||||
throw new RuntimeException("habit not in database");
|
// throw new RuntimeException("habit not in database");
|
||||||
|
//
|
||||||
record.position = i++;
|
// record.position = i++;
|
||||||
record.save();
|
// repository.save(record);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
update(habits);
|
// update(habits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(@NonNull Habit habit)
|
public synchronized void remove(@NonNull Habit habit)
|
||||||
{
|
{
|
||||||
if (!cache.containsKey(habit.getId()))
|
loadRecords();
|
||||||
throw new RuntimeException("habit not in cache");
|
list.remove(habit);
|
||||||
|
|
||||||
cache.remove(habit.getId());
|
HabitRecord record = repository.find(habit.getId());
|
||||||
HabitRecord record = HabitRecord.get(habit.getId());
|
|
||||||
if (record == null) throw new RuntimeException("habit not in database");
|
if (record == null) throw new RuntimeException("habit not in database");
|
||||||
record.cascadeDelete();
|
executeAsTransaction(() ->
|
||||||
|
{
|
||||||
|
((SQLiteRepetitionList) habit.getRepetitions()).removeAll();
|
||||||
|
repository.remove(record);
|
||||||
|
});
|
||||||
rebuildOrder();
|
rebuildOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAll()
|
public synchronized void removeAll()
|
||||||
{
|
{
|
||||||
sqlite.query("delete from repetitions", null);
|
loadRecords();
|
||||||
sqlite.query("delete from habits", null);
|
list.removeAll();
|
||||||
|
SQLiteDatabase db = Cache.openDatabase();
|
||||||
|
db.execSQL("delete from habits");
|
||||||
|
db.execSQL("delete from repetitions");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void reorder(Habit from, Habit to)
|
public synchronized void reorder(Habit from, Habit to)
|
||||||
{
|
{
|
||||||
if (from == to) return;
|
loadRecords();
|
||||||
|
list.reorder(from, to);
|
||||||
|
|
||||||
HabitRecord fromRecord = HabitRecord.get(from.getId());
|
HabitRecord fromRecord = repository.find(from.getId());
|
||||||
HabitRecord toRecord = HabitRecord.get(to.getId());
|
HabitRecord toRecord = repository.find(to.getId());
|
||||||
|
|
||||||
if (fromRecord == null)
|
if (fromRecord == null)
|
||||||
throw new RuntimeException("habit not in database");
|
throw new RuntimeException("habit not in database");
|
||||||
@@ -207,27 +206,22 @@ public class SQLiteHabitList extends HabitList
|
|||||||
|
|
||||||
Integer fromPos = fromRecord.position;
|
Integer fromPos = fromRecord.position;
|
||||||
Integer toPos = toRecord.position;
|
Integer toPos = toRecord.position;
|
||||||
|
SQLiteDatabase db = Cache.openDatabase();
|
||||||
Log.d("SQLiteHabitList",
|
|
||||||
String.format("reorder: %d %d", fromPos, toPos));
|
|
||||||
|
|
||||||
if (toPos < fromPos)
|
if (toPos < fromPos)
|
||||||
{
|
{
|
||||||
new Update(HabitRecord.class)
|
db.execSQL("update habits set position = position + 1 " +
|
||||||
.set("position = position + 1")
|
"where position >= ? and position < ?",
|
||||||
.where("position >= ? and position < ?", toPos, fromPos)
|
new String[]{ toPos.toString(), fromPos.toString() });
|
||||||
.execute();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
new Update(HabitRecord.class)
|
db.execSQL("update habits set position = position - 1 " +
|
||||||
.set("position = position - 1")
|
"where position > ? and position <= ?",
|
||||||
.where("position > ? and position <= ?", fromPos, toPos)
|
new String[]{ fromPos.toString(), toPos.toString() });
|
||||||
.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fromRecord.position = toPos;
|
fromRecord.position = toPos;
|
||||||
fromRecord.save();
|
repository.save(fromRecord);
|
||||||
update(from);
|
update(from);
|
||||||
getObservable().notifyListeners();
|
getObservable().notifyListeners();
|
||||||
}
|
}
|
||||||
@@ -235,100 +229,33 @@ public class SQLiteHabitList extends HabitList
|
|||||||
@Override
|
@Override
|
||||||
public void repair()
|
public void repair()
|
||||||
{
|
{
|
||||||
super.repair();
|
loadRecords();
|
||||||
rebuildOrder();
|
rebuildOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size()
|
public int size()
|
||||||
{
|
{
|
||||||
return toList().size();
|
loadRecords();
|
||||||
|
return list.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(List<Habit> habits)
|
public synchronized void update(List<Habit> habits)
|
||||||
{
|
{
|
||||||
|
loadRecords();
|
||||||
for (Habit h : habits)
|
for (Habit h : habits)
|
||||||
{
|
{
|
||||||
HabitRecord record = HabitRecord.get(h.getId());
|
HabitRecord record = repository.find(h.getId());
|
||||||
if (record == null)
|
if (record == null)
|
||||||
throw new RuntimeException("habit not in database");
|
throw new RuntimeException("habit not in database");
|
||||||
record.copyFrom(h);
|
record.copyFrom(h);
|
||||||
record.save();
|
repository.save(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized List<Habit> toList()
|
public void reload()
|
||||||
{
|
{
|
||||||
String query = buildSelectQuery();
|
loaded = false;
|
||||||
List<HabitRecord> recordList = sqlite.query(query, null);
|
|
||||||
|
|
||||||
List<Habit> habits = new LinkedList<>();
|
|
||||||
for (HabitRecord record : recordList)
|
|
||||||
{
|
|
||||||
Habit habit = getById(record.getId());
|
|
||||||
if (habit == null) continue;
|
|
||||||
if (!filter.matches(habit)) continue;
|
|
||||||
habits.add(habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(order == Order.BY_SCORE)
|
|
||||||
{
|
|
||||||
Collections.sort(habits, (lhs, rhs) -> {
|
|
||||||
double s1 = lhs.getScores().getTodayValue();
|
|
||||||
double s2 = rhs.getScores().getTodayValue();
|
|
||||||
return Double.compare(s2, s1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return habits;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendOrderBy(StringBuilder query)
|
|
||||||
{
|
|
||||||
switch (order)
|
|
||||||
{
|
|
||||||
case BY_POSITION:
|
|
||||||
query.append("order by position ");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BY_NAME:
|
|
||||||
case BY_SCORE:
|
|
||||||
query.append("order by name ");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BY_COLOR:
|
|
||||||
query.append("order by color, name ");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendSelect(StringBuilder query)
|
|
||||||
{
|
|
||||||
query.append(HabitRecord.SELECT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendWhere(StringBuilder query)
|
|
||||||
{
|
|
||||||
ArrayList<Object> where = new ArrayList<>();
|
|
||||||
if (filter.isReminderRequired()) where.add("reminder_hour is not null");
|
|
||||||
if (!filter.isArchivedAllowed()) where.add("archived = 0");
|
|
||||||
|
|
||||||
if (where.isEmpty()) return;
|
|
||||||
query.append("where ");
|
|
||||||
query.append(StringUtils.join(where, " and "));
|
|
||||||
query.append(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildSelectQuery()
|
|
||||||
{
|
|
||||||
StringBuilder query = new StringBuilder();
|
|
||||||
appendSelect(query);
|
|
||||||
appendWhere(query);
|
|
||||||
appendOrderBy(query);
|
|
||||||
return query.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,15 +19,14 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.models.sqlite;
|
package org.isoron.uhabits.models.sqlite;
|
||||||
|
|
||||||
import android.database.*;
|
|
||||||
import android.database.sqlite.*;
|
|
||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import com.activeandroid.*;
|
import com.activeandroid.*;
|
||||||
import com.activeandroid.query.*;
|
|
||||||
|
|
||||||
|
import org.isoron.androidbase.storage.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.isoron.uhabits.core.models.memory.*;
|
||||||
import org.isoron.uhabits.models.sqlite.records.*;
|
import org.isoron.uhabits.models.sqlite.records.*;
|
||||||
import org.jetbrains.annotations.*;
|
import org.jetbrains.annotations.*;
|
||||||
|
|
||||||
@@ -38,165 +37,112 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public class SQLiteRepetitionList extends RepetitionList
|
public class SQLiteRepetitionList extends RepetitionList
|
||||||
{
|
{
|
||||||
|
private final SQLiteRepository<RepetitionRecord> repository;
|
||||||
|
|
||||||
private final SQLiteUtils<RepetitionRecord> sqlite;
|
private final MemoryRepetitionList list;
|
||||||
|
|
||||||
@Nullable
|
private boolean loaded = false;
|
||||||
private HabitRecord habitRecord;
|
|
||||||
|
|
||||||
private SQLiteStatement addStatement;
|
|
||||||
|
|
||||||
public static final String ADD_QUERY =
|
|
||||||
"insert into repetitions(habit, timestamp, value) " +
|
|
||||||
"values (?,?,?)";
|
|
||||||
|
|
||||||
public SQLiteRepetitionList(@NonNull Habit habit)
|
public SQLiteRepetitionList(@NonNull Habit habit)
|
||||||
{
|
{
|
||||||
super(habit);
|
super(habit);
|
||||||
sqlite = new SQLiteUtils<>(RepetitionRecord.class);
|
repository = new SQLiteRepository<>(RepetitionRecord.class,
|
||||||
|
Cache.openDatabase());
|
||||||
SQLiteDatabase db = Cache.openDatabase();
|
list = new MemoryRepetitionList(habit);
|
||||||
addStatement = db.compileStatement(ADD_QUERY);
|
}
|
||||||
|
|
||||||
|
private void loadRecords()
|
||||||
|
{
|
||||||
|
if (loaded) return;
|
||||||
|
loaded = true;
|
||||||
|
|
||||||
|
check(habit.getId());
|
||||||
|
List<RepetitionRecord> records =
|
||||||
|
repository.findAll("where habit = ? order by timestamp",
|
||||||
|
habit.getId().toString());
|
||||||
|
|
||||||
|
for (RepetitionRecord rec : records)
|
||||||
|
list.add(rec.toRepetition());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
@Override
|
||||||
public void add(Repetition rep)
|
public void add(Repetition rep)
|
||||||
{
|
{
|
||||||
|
loadRecords();
|
||||||
|
list.add(rep);
|
||||||
check(habit.getId());
|
check(habit.getId());
|
||||||
addStatement.bindLong(1, habit.getId());
|
RepetitionRecord record = new RepetitionRecord();
|
||||||
addStatement.bindLong(2, rep.getTimestamp());
|
record.habit_id = habit.getId();
|
||||||
addStatement.bindLong(3, rep.getValue());
|
record.copyFrom(rep);
|
||||||
addStatement.execute();
|
repository.save(record);
|
||||||
observable.notifyListeners();
|
observable.notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Repetition> getByInterval(long timeFrom, long timeTo)
|
public List<Repetition> getByInterval(long timeFrom, long timeTo)
|
||||||
{
|
{
|
||||||
check(habit.getId());
|
loadRecords();
|
||||||
String query = "select habit, timestamp, value " +
|
return list.getByInterval(timeFrom, timeTo);
|
||||||
"from Repetitions " +
|
|
||||||
"where habit = ? and timestamp >= ? and timestamp <= ? " +
|
|
||||||
"order by timestamp";
|
|
||||||
|
|
||||||
String params[] = {
|
|
||||||
Long.toString(habit.getId()),
|
|
||||||
Long.toString(timeFrom),
|
|
||||||
Long.toString(timeTo)
|
|
||||||
};
|
|
||||||
|
|
||||||
List<RepetitionRecord> records = sqlite.query(query, params);
|
|
||||||
return toRepetitions(records);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Repetition getByTimestamp(long timestamp)
|
public Repetition getByTimestamp(long timestamp)
|
||||||
{
|
{
|
||||||
check(habit.getId());
|
loadRecords();
|
||||||
String query = "select habit, timestamp, value " +
|
return list.getByTimestamp(timestamp);
|
||||||
"from Repetitions " +
|
|
||||||
"where habit = ? and timestamp = ? " +
|
|
||||||
"limit 1";
|
|
||||||
|
|
||||||
String params[] =
|
|
||||||
{ Long.toString(habit.getId()), Long.toString(timestamp) };
|
|
||||||
|
|
||||||
RepetitionRecord record = sqlite.querySingle(query, params);
|
|
||||||
if (record == null) return null;
|
|
||||||
record.habit = habitRecord;
|
|
||||||
return record.toRepetition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Repetition getOldest()
|
public Repetition getOldest()
|
||||||
{
|
{
|
||||||
check(habit.getId());
|
loadRecords();
|
||||||
String query = "select habit, timestamp, value " +
|
return list.getOldest();
|
||||||
"from Repetitions " +
|
|
||||||
"where habit = ? " +
|
|
||||||
"order by timestamp asc " +
|
|
||||||
"limit 1";
|
|
||||||
|
|
||||||
String params[] = { Long.toString(habit.getId()) };
|
|
||||||
|
|
||||||
RepetitionRecord record = sqlite.querySingle(query, params);
|
|
||||||
if (record == null) return null;
|
|
||||||
record.habit = habitRecord;
|
|
||||||
return record.toRepetition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Repetition getNewest()
|
public Repetition getNewest()
|
||||||
{
|
{
|
||||||
check(habit.getId());
|
loadRecords();
|
||||||
String query = "select habit, timestamp, value " +
|
return list.getNewest();
|
||||||
"from Repetitions " +
|
|
||||||
"where habit = ? " +
|
|
||||||
"order by timestamp desc " +
|
|
||||||
"limit 1";
|
|
||||||
|
|
||||||
String params[] = { Long.toString(habit.getId()) };
|
|
||||||
|
|
||||||
RepetitionRecord record = sqlite.querySingle(query, params);
|
|
||||||
if (record == null) return null;
|
|
||||||
record.habit = habitRecord;
|
|
||||||
return record.toRepetition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(@NonNull Repetition repetition)
|
public void remove(@NonNull Repetition repetition)
|
||||||
{
|
{
|
||||||
new Delete()
|
loadRecords();
|
||||||
.from(RepetitionRecord.class)
|
list.remove(repetition);
|
||||||
.where("habit = ?", habit.getId())
|
check(habit.getId());
|
||||||
.and("timestamp = ?", repetition.getTimestamp())
|
repository.execSQL(
|
||||||
.execute();
|
"delete from repetitions where habit = ? and timestamp = ?",
|
||||||
|
habit.getId());
|
||||||
observable.notifyListeners();
|
observable.notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("null -> fail")
|
public void removeAll()
|
||||||
private void check(Long id)
|
|
||||||
{
|
|
||||||
if (id == null) throw new RuntimeException("habit is not saved");
|
|
||||||
|
|
||||||
if (habitRecord != null) return;
|
|
||||||
|
|
||||||
habitRecord = HabitRecord.get(id);
|
|
||||||
if (habitRecord == null) throw new RuntimeException("habit not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private List<Repetition> toRepetitions(
|
|
||||||
@NonNull List<RepetitionRecord> records)
|
|
||||||
{
|
{
|
||||||
|
loadRecords();
|
||||||
|
list.removeAll();
|
||||||
check(habit.getId());
|
check(habit.getId());
|
||||||
|
repository.execSQL("delete from repetitions where habit = ?",
|
||||||
List<Repetition> reps = new LinkedList<>();
|
habit.getId());
|
||||||
for (RepetitionRecord record : records)
|
|
||||||
{
|
|
||||||
record.habit = habitRecord;
|
|
||||||
reps.add(record.toRepetition());
|
|
||||||
}
|
|
||||||
|
|
||||||
return reps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getTotalCount()
|
public long getTotalCount()
|
||||||
{
|
{
|
||||||
SQLiteDatabase db = Cache.openDatabase();
|
loadRecords();
|
||||||
|
return list.getTotalCount();
|
||||||
|
}
|
||||||
|
|
||||||
return DatabaseUtils.queryNumEntries(db, "Repetitions",
|
public void reload()
|
||||||
"habit=?", new String[] { Long.toString(habit.getId()) });
|
{
|
||||||
|
loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("null -> fail")
|
||||||
|
private void check(Long value)
|
||||||
|
{
|
||||||
|
if (value == null) throw new RuntimeException("null check failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,84 +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.models.sqlite;
|
|
||||||
|
|
||||||
import android.database.*;
|
|
||||||
import android.database.sqlite.*;
|
|
||||||
import android.support.annotation.*;
|
|
||||||
|
|
||||||
import com.activeandroid.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.models.sqlite.records.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class SQLiteUtils<T extends SQLiteRecord>
|
|
||||||
{
|
|
||||||
private Class klass;
|
|
||||||
|
|
||||||
public SQLiteUtils(Class klass)
|
|
||||||
{
|
|
||||||
this.klass = klass;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public List<T> query(String query, String params[])
|
|
||||||
{
|
|
||||||
SQLiteDatabase db = Cache.openDatabase();
|
|
||||||
try (Cursor c = db.rawQuery(query, params))
|
|
||||||
{
|
|
||||||
return cursorToMultipleRecords(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public T querySingle(String query, String params[])
|
|
||||||
{
|
|
||||||
SQLiteDatabase db = Cache.openDatabase();
|
|
||||||
try(Cursor c = db.rawQuery(query, params))
|
|
||||||
{
|
|
||||||
if (!c.moveToNext()) return null;
|
|
||||||
return cursorToSingleRecord(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private List<T> cursorToMultipleRecords(Cursor c)
|
|
||||||
{
|
|
||||||
List<T> records = new LinkedList<>();
|
|
||||||
while (c.moveToNext()) records.add(cursorToSingleRecord(c));
|
|
||||||
return records;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private T cursorToSingleRecord(Cursor c)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
T record = (T) klass.newInstance();
|
|
||||||
record.copyFrom(c);
|
|
||||||
return record;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,122 +19,85 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.models.sqlite.records;
|
package org.isoron.uhabits.models.sqlite.records;
|
||||||
|
|
||||||
import android.annotation.*;
|
|
||||||
import android.database.*;
|
|
||||||
import android.support.annotation.*;
|
|
||||||
|
|
||||||
import com.activeandroid.*;
|
import com.activeandroid.*;
|
||||||
import com.activeandroid.annotation.*;
|
|
||||||
import com.activeandroid.query.*;
|
|
||||||
import com.activeandroid.util.*;
|
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.builder.*;
|
||||||
|
import org.isoron.androidbase.storage.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
import org.isoron.uhabits.utils.DatabaseUtils;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SQLite database record corresponding to a {@link Habit}.
|
* The SQLite database record corresponding to a {@link Habit}.
|
||||||
*/
|
*/
|
||||||
@Table(name = "Habits")
|
@Table(name = "habits")
|
||||||
public class HabitRecord extends Model implements SQLiteRecord
|
@com.activeandroid.annotation.Table(name = "Habits")
|
||||||
|
public class HabitRecord extends Model
|
||||||
{
|
{
|
||||||
public static String SELECT =
|
@Column
|
||||||
"select id, color, description, freq_den, freq_num, " +
|
@com.activeandroid.annotation.Column
|
||||||
"name, position, reminder_hour, reminder_min, " +
|
|
||||||
"highlight, archived, reminder_days, type, target_type, " +
|
|
||||||
"target_value, unit from habits ";
|
|
||||||
|
|
||||||
@Column(name = "name")
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
@Column(name = "description")
|
|
||||||
public String description;
|
public String description;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@com.activeandroid.annotation.Column
|
||||||
|
public String name;
|
||||||
|
|
||||||
@Column(name = "freq_num")
|
@Column(name = "freq_num")
|
||||||
public int freqNum;
|
@com.activeandroid.annotation.Column(name = "freq_num")
|
||||||
|
public Integer freqNum;
|
||||||
|
|
||||||
@Column(name = "freq_den")
|
@Column(name = "freq_den")
|
||||||
public int freqDen;
|
@com.activeandroid.annotation.Column(name = "freq_den")
|
||||||
|
public Integer freqDen;
|
||||||
|
|
||||||
@Column(name = "color")
|
@Column
|
||||||
public int color;
|
@com.activeandroid.annotation.Column
|
||||||
|
public Integer color;
|
||||||
|
|
||||||
@Column(name = "position")
|
@Column
|
||||||
public int position;
|
@com.activeandroid.annotation.Column
|
||||||
|
public Integer position;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Column(name = "reminder_hour")
|
@Column(name = "reminder_hour")
|
||||||
|
@com.activeandroid.annotation.Column(name = "reminder_hour")
|
||||||
public Integer reminderHour;
|
public Integer reminderHour;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Column(name = "reminder_min")
|
@Column(name = "reminder_min")
|
||||||
|
@com.activeandroid.annotation.Column(name = "reminder_min")
|
||||||
public Integer reminderMin;
|
public Integer reminderMin;
|
||||||
|
|
||||||
@Column(name = "reminder_days")
|
@Column(name = "reminder_days")
|
||||||
public int reminderDays;
|
@com.activeandroid.annotation.Column(name = "reminder_days")
|
||||||
|
public Integer reminderDays;
|
||||||
|
|
||||||
@Column(name = "highlight")
|
@Column
|
||||||
public int highlight;
|
@com.activeandroid.annotation.Column
|
||||||
|
public Integer highlight;
|
||||||
|
|
||||||
@Column(name = "archived")
|
@Column
|
||||||
public int archived;
|
@com.activeandroid.annotation.Column
|
||||||
|
public Integer archived;
|
||||||
|
|
||||||
@Column(name = "type")
|
@Column
|
||||||
public int type;
|
@com.activeandroid.annotation.Column
|
||||||
|
public Integer type;
|
||||||
|
|
||||||
@Column(name = "target_value")
|
@Column(name = "target_value")
|
||||||
public double targetValue;
|
@com.activeandroid.annotation.Column(name = "target_value")
|
||||||
|
public Double targetValue;
|
||||||
|
|
||||||
@Column(name = "target_type")
|
@Column(name = "target_type")
|
||||||
public int targetType;
|
@com.activeandroid.annotation.Column(name = "target_type")
|
||||||
|
public Integer targetType;
|
||||||
|
|
||||||
@Column(name = "unit")
|
@Column
|
||||||
|
@com.activeandroid.annotation.Column
|
||||||
public String unit;
|
public String unit;
|
||||||
|
|
||||||
public HabitRecord()
|
@Column
|
||||||
{
|
public Long id;
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static HabitRecord get(long id)
|
|
||||||
{
|
|
||||||
return HabitRecord.load(HabitRecord.class, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the id of a habit on the database.
|
|
||||||
*
|
|
||||||
* @param oldId the original id
|
|
||||||
* @param newId the new id
|
|
||||||
*/
|
|
||||||
@SuppressLint("DefaultLocale")
|
|
||||||
public static void updateId(long oldId, long newId)
|
|
||||||
{
|
|
||||||
SQLiteUtils.execSql(
|
|
||||||
String.format("update Habits set Id = %d where Id = %d", newId,
|
|
||||||
oldId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the habit and all data associated to it, including checkmarks,
|
|
||||||
* repetitions and scores.
|
|
||||||
*/
|
|
||||||
public void cascadeDelete()
|
|
||||||
{
|
|
||||||
Long id = getId();
|
|
||||||
|
|
||||||
DatabaseUtils.executeAsTransaction(() -> {
|
|
||||||
new Delete()
|
|
||||||
.from(RepetitionRecord.class)
|
|
||||||
.where("habit = ?", id)
|
|
||||||
.execute();
|
|
||||||
delete();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void copyFrom(Habit model)
|
public void copyFrom(Habit model)
|
||||||
{
|
{
|
||||||
|
this.id = model.getId();
|
||||||
this.name = model.getName();
|
this.name = model.getName();
|
||||||
this.description = model.getDescription();
|
this.description = model.getDescription();
|
||||||
this.highlight = 0;
|
this.highlight = 0;
|
||||||
@@ -161,35 +124,14 @@ public class HabitRecord extends Model implements SQLiteRecord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void copyFrom(Cursor c)
|
|
||||||
{
|
|
||||||
setId(c.getLong(0));
|
|
||||||
color = c.getInt(1);
|
|
||||||
description = c.getString(2);
|
|
||||||
freqDen = c.getInt(3);
|
|
||||||
freqNum = c.getInt(4);
|
|
||||||
name = c.getString(5);
|
|
||||||
position = c.getInt(6);
|
|
||||||
reminderHour = c.getInt(7);
|
|
||||||
reminderMin = c.getInt(8);
|
|
||||||
highlight = c.getInt(9);
|
|
||||||
archived = c.getInt(10);
|
|
||||||
reminderDays = c.getInt(11);
|
|
||||||
type = c.getInt(12);
|
|
||||||
targetType = c.getInt(13);
|
|
||||||
targetValue = c.getDouble(14);
|
|
||||||
unit = c.getString(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void copyTo(Habit habit)
|
public void copyTo(Habit habit)
|
||||||
{
|
{
|
||||||
|
habit.setId(this.id);
|
||||||
habit.setName(this.name);
|
habit.setName(this.name);
|
||||||
habit.setDescription(this.description);
|
habit.setDescription(this.description);
|
||||||
habit.setFrequency(new Frequency(this.freqNum, this.freqDen));
|
habit.setFrequency(new Frequency(this.freqNum, this.freqDen));
|
||||||
habit.setColor(this.color);
|
habit.setColor(this.color);
|
||||||
habit.setArchived(this.archived != 0);
|
habit.setArchived(this.archived != 0);
|
||||||
habit.setId(this.getId());
|
|
||||||
habit.setType(this.type);
|
habit.setType(this.type);
|
||||||
habit.setTargetType(this.targetType);
|
habit.setTargetType(this.targetType);
|
||||||
habit.setTargetValue(this.targetValue);
|
habit.setTargetValue(this.targetValue);
|
||||||
@@ -202,28 +144,77 @@ public class HabitRecord extends Model implements SQLiteRecord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Saves the habit on the database, and assigns the specified id to it.
|
public boolean equals(Object o)
|
||||||
*
|
|
||||||
* @param id the id that the habit should receive
|
|
||||||
*/
|
|
||||||
public void save(long id)
|
|
||||||
{
|
{
|
||||||
save();
|
if (this == o) return true;
|
||||||
updateId(getId(), id);
|
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
HabitRecord that = (HabitRecord) o;
|
||||||
|
|
||||||
|
return new EqualsBuilder()
|
||||||
|
.appendSuper(super.equals(o))
|
||||||
|
.append(freqNum, that.freqNum)
|
||||||
|
.append(freqDen, that.freqDen)
|
||||||
|
.append(color, that.color)
|
||||||
|
.append(position, that.position)
|
||||||
|
.append(reminderDays, that.reminderDays)
|
||||||
|
.append(highlight, that.highlight)
|
||||||
|
.append(archived, that.archived)
|
||||||
|
.append(type, that.type)
|
||||||
|
.append(targetValue, that.targetValue)
|
||||||
|
.append(targetType, that.targetType)
|
||||||
|
.append(name, that.name)
|
||||||
|
.append(description, that.description)
|
||||||
|
.append(reminderHour, that.reminderHour)
|
||||||
|
.append(reminderMin, that.reminderMin)
|
||||||
|
.append(unit, that.unit)
|
||||||
|
.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setId(Long id)
|
@Override
|
||||||
|
public int hashCode()
|
||||||
{
|
{
|
||||||
try
|
return new HashCodeBuilder(17, 37)
|
||||||
{
|
.appendSuper(super.hashCode())
|
||||||
Field f = (Model.class).getDeclaredField("mId");
|
.append(name)
|
||||||
f.setAccessible(true);
|
.append(description)
|
||||||
f.set(this, id);
|
.append(freqNum)
|
||||||
}
|
.append(freqDen)
|
||||||
catch (Exception e)
|
.append(color)
|
||||||
{
|
.append(position)
|
||||||
throw new RuntimeException(e);
|
.append(reminderHour)
|
||||||
}
|
.append(reminderMin)
|
||||||
|
.append(reminderDays)
|
||||||
|
.append(highlight)
|
||||||
|
.append(archived)
|
||||||
|
.append(type)
|
||||||
|
.append(targetValue)
|
||||||
|
.append(targetType)
|
||||||
|
.append(unit)
|
||||||
|
.toHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return new ToStringBuilder(this)
|
||||||
|
.append("name", name)
|
||||||
|
.append("description", description)
|
||||||
|
.append("freqNum", freqNum)
|
||||||
|
.append("freqDen", freqDen)
|
||||||
|
.append("color", color)
|
||||||
|
.append("position", position)
|
||||||
|
.append("reminderHour", reminderHour)
|
||||||
|
.append("reminderMin", reminderMin)
|
||||||
|
.append("reminderDays", reminderDays)
|
||||||
|
.append("highlight", highlight)
|
||||||
|
.append("archived", archived)
|
||||||
|
.append("type", type)
|
||||||
|
.append("targetValue", targetValue)
|
||||||
|
.append("targetType", targetType)
|
||||||
|
.append("unit", unit)
|
||||||
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,32 +19,34 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.models.sqlite.records;
|
package org.isoron.uhabits.models.sqlite.records;
|
||||||
|
|
||||||
import android.database.*;
|
|
||||||
|
|
||||||
import com.activeandroid.*;
|
import com.activeandroid.*;
|
||||||
import com.activeandroid.annotation.*;
|
|
||||||
|
|
||||||
|
import org.isoron.androidbase.storage.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SQLite database record corresponding to a {@link Repetition}.
|
* The SQLite database record corresponding to a {@link Repetition}.
|
||||||
*/
|
*/
|
||||||
@Table(name = "Repetitions")
|
@Table(name = "Repetitions")
|
||||||
public class RepetitionRecord extends Model implements SQLiteRecord
|
@com.activeandroid.annotation.Table(name = "Repetitions")
|
||||||
|
public class RepetitionRecord extends Model
|
||||||
{
|
{
|
||||||
@Column(name = "habit")
|
@com.activeandroid.annotation.Column(name = "habit")
|
||||||
public HabitRecord habit;
|
public HabitRecord habit;
|
||||||
|
|
||||||
@Column(name = "timestamp")
|
@Column(name = "habit")
|
||||||
|
public Long habit_id;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@com.activeandroid.annotation.Column(name = "timestamp")
|
||||||
public Long timestamp;
|
public Long timestamp;
|
||||||
|
|
||||||
@Column(name = "value")
|
@Column
|
||||||
public int value;
|
@com.activeandroid.annotation.Column(name = "value")
|
||||||
|
public Integer value;
|
||||||
|
|
||||||
public static RepetitionRecord get(Long id)
|
@Column
|
||||||
{
|
public Long id;
|
||||||
return RepetitionRecord.load(RepetitionRecord.class, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void copyFrom(Repetition repetition)
|
public void copyFrom(Repetition repetition)
|
||||||
{
|
{
|
||||||
@@ -52,13 +54,6 @@ public class RepetitionRecord extends Model implements SQLiteRecord
|
|||||||
value = repetition.getValue();
|
value = repetition.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void copyFrom(Cursor c)
|
|
||||||
{
|
|
||||||
timestamp = c.getLong(1);
|
|
||||||
value = c.getInt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Repetition toRepetition()
|
public Repetition toRepetition()
|
||||||
{
|
{
|
||||||
return new Repetition(timestamp, value);
|
return new Repetition(timestamp, value);
|
||||||
|
|||||||
@@ -1,27 +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.models.sqlite.records;
|
|
||||||
|
|
||||||
import android.database.*;
|
|
||||||
|
|
||||||
public interface SQLiteRecord
|
|
||||||
{
|
|
||||||
void copyFrom(Cursor c);
|
|
||||||
}
|
|
||||||
@@ -221,4 +221,6 @@ public abstract class RepetitionList
|
|||||||
add(new Repetition(timestamp, value));
|
add(new Repetition(timestamp, value));
|
||||||
habit.invalidateNewerThan(timestamp);
|
habit.invalidateNewerThan(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void removeAll();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package org.isoron.uhabits.core.models.memory;
|
|||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.isoron.uhabits.core.utils.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -60,8 +61,10 @@ public class MemoryCheckmarkList extends CheckmarkList
|
|||||||
Checkmark oldest = getOldestComputed();
|
Checkmark oldest = getOldestComputed();
|
||||||
if(newest != null) newestTimestamp = newest.getTimestamp();
|
if(newest != null) newestTimestamp = newest.getTimestamp();
|
||||||
if(oldest != null) oldestTimestamp = oldest.getTimestamp();
|
if(oldest != null) oldestTimestamp = oldest.getTimestamp();
|
||||||
|
long days = (newestTimestamp - oldestTimestamp) /
|
||||||
|
DateUtils.millisecondsInOneDay;
|
||||||
|
|
||||||
List<Checkmark> filtered = new LinkedList<>();
|
List<Checkmark> filtered = new ArrayList<>((int) days);
|
||||||
for(long time = toTimestamp; time >= fromTimestamp; time -= millisecondsInOneDay)
|
for(long time = toTimestamp; time >= fromTimestamp; time -= millisecondsInOneDay)
|
||||||
{
|
{
|
||||||
if(time > newestTimestamp || time < oldestTimestamp)
|
if(time > newestTimestamp || time < oldestTimestamp)
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ import org.isoron.uhabits.core.models.*;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static org.isoron.uhabits.core.models.HabitList.Order.*;
|
import static org.isoron.uhabits.core.models.HabitList.Order.BY_COLOR;
|
||||||
|
import static org.isoron.uhabits.core.models.HabitList.Order.BY_NAME;
|
||||||
|
import static org.isoron.uhabits.core.models.HabitList.Order.BY_POSITION;
|
||||||
|
import static org.isoron.uhabits.core.models.HabitList.Order.BY_SCORE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In-memory implementation of {@link HabitList}.
|
* In-memory implementation of {@link HabitList}.
|
||||||
@@ -55,7 +58,8 @@ public class MemoryHabitList extends HabitList
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(@NonNull Habit habit) throws IllegalArgumentException
|
public synchronized void add(@NonNull Habit habit)
|
||||||
|
throws IllegalArgumentException
|
||||||
{
|
{
|
||||||
if (list.contains(habit))
|
if (list.contains(habit))
|
||||||
throw new IllegalArgumentException("habit already added");
|
throw new IllegalArgumentException("habit already added");
|
||||||
@@ -70,7 +74,7 @@ public class MemoryHabitList extends HabitList
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Habit getById(long id)
|
public synchronized Habit getById(long id)
|
||||||
{
|
{
|
||||||
for (Habit h : list)
|
for (Habit h : list)
|
||||||
{
|
{
|
||||||
@@ -82,14 +86,14 @@ public class MemoryHabitList extends HabitList
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Habit getByPosition(int position)
|
public synchronized Habit getByPosition(int position)
|
||||||
{
|
{
|
||||||
return list.get(position);
|
return list.get(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public HabitList getFiltered(HabitMatcher matcher)
|
public synchronized HabitList getFiltered(HabitMatcher matcher)
|
||||||
{
|
{
|
||||||
MemoryHabitList habits = new MemoryHabitList(matcher);
|
MemoryHabitList habits = new MemoryHabitList(matcher);
|
||||||
habits.comparator = comparator;
|
habits.comparator = comparator;
|
||||||
@@ -98,11 +102,19 @@ public class MemoryHabitList extends HabitList
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Order getOrder()
|
public synchronized Order getOrder()
|
||||||
{
|
{
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setOrder(@NonNull Order order)
|
||||||
|
{
|
||||||
|
this.order = order;
|
||||||
|
this.comparator = getComparatorByOrder(order);
|
||||||
|
resort();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int indexOf(@NonNull Habit h)
|
public int indexOf(@NonNull Habit h)
|
||||||
{
|
{
|
||||||
@@ -116,27 +128,19 @@ public class MemoryHabitList extends HabitList
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(@NonNull Habit habit)
|
public synchronized void remove(@NonNull Habit habit)
|
||||||
{
|
{
|
||||||
list.remove(habit);
|
list.remove(habit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reorder(Habit from, Habit to)
|
public synchronized void reorder(Habit from, Habit to)
|
||||||
{
|
{
|
||||||
int toPos = indexOf(to);
|
int toPos = indexOf(to);
|
||||||
list.remove(from);
|
list.remove(from);
|
||||||
list.add(toPos, from);
|
list.add(toPos, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setOrder(@NonNull Order order)
|
|
||||||
{
|
|
||||||
this.order = order;
|
|
||||||
this.comparator = getComparatorByOrder(order);
|
|
||||||
resort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size()
|
public int size()
|
||||||
{
|
{
|
||||||
@@ -154,14 +158,16 @@ public class MemoryHabitList extends HabitList
|
|||||||
Comparator<Habit> nameComparator =
|
Comparator<Habit> nameComparator =
|
||||||
(h1, h2) -> h1.getName().compareTo(h2.getName());
|
(h1, h2) -> h1.getName().compareTo(h2.getName());
|
||||||
|
|
||||||
Comparator<Habit> colorComparator = (h1, h2) -> {
|
Comparator<Habit> colorComparator = (h1, h2) ->
|
||||||
|
{
|
||||||
Integer c1 = h1.getColor();
|
Integer c1 = h1.getColor();
|
||||||
Integer c2 = h2.getColor();
|
Integer c2 = h2.getColor();
|
||||||
if (c1.equals(c2)) return nameComparator.compare(h1, h2);
|
if (c1.equals(c2)) return nameComparator.compare(h1, h2);
|
||||||
else return c1.compareTo(c2);
|
else return c1.compareTo(c2);
|
||||||
};
|
};
|
||||||
|
|
||||||
Comparator<Habit> scoreComparator = (h1, h2) -> {
|
Comparator<Habit> scoreComparator = (h1, h2) ->
|
||||||
|
{
|
||||||
double s1 = h1.getScores().getTodayValue();
|
double s1 = h1.getScores().getTodayValue();
|
||||||
double s2 = h2.getScores().getTodayValue();
|
double s2 = h2.getScores().getTodayValue();
|
||||||
return Double.compare(s2, s1);
|
return Double.compare(s2, s1);
|
||||||
@@ -174,7 +180,7 @@ public class MemoryHabitList extends HabitList
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resort()
|
private synchronized void resort()
|
||||||
{
|
{
|
||||||
if (comparator != null) Collections.sort(list, comparator);
|
if (comparator != null) Collections.sort(list, comparator);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public class MemoryRepetitionList extends RepetitionList
|
public class MemoryRepetitionList extends RepetitionList
|
||||||
{
|
{
|
||||||
LinkedList<Repetition> list;
|
ArrayList<Repetition> list;
|
||||||
|
|
||||||
public MemoryRepetitionList(Habit habit)
|
public MemoryRepetitionList(Habit habit)
|
||||||
{
|
{
|
||||||
super(habit);
|
super(habit);
|
||||||
list = new LinkedList<>();
|
list = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -48,7 +48,7 @@ public class MemoryRepetitionList extends RepetitionList
|
|||||||
@Override
|
@Override
|
||||||
public List<Repetition> getByInterval(long fromTimestamp, long toTimestamp)
|
public List<Repetition> getByInterval(long fromTimestamp, long toTimestamp)
|
||||||
{
|
{
|
||||||
LinkedList<Repetition> filtered = new LinkedList<>();
|
ArrayList<Repetition> filtered = new ArrayList<>();
|
||||||
|
|
||||||
for (Repetition r : list)
|
for (Repetition r : list)
|
||||||
{
|
{
|
||||||
@@ -57,7 +57,7 @@ public class MemoryRepetitionList extends RepetitionList
|
|||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(filtered,
|
Collections.sort(filtered,
|
||||||
(r1, r2) -> (int) (r1.getTimestamp() - r2.getTimestamp()));
|
(r1, r2) -> Long.compare(r1.getTimestamp(), r2.getTimestamp()));
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
@@ -122,4 +122,10 @@ public class MemoryRepetitionList extends RepetitionList
|
|||||||
{
|
{
|
||||||
return list.size();
|
return list.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll()
|
||||||
|
{
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user