Move models.sqlite to uhabits-core

pull/312/head
Alinson S. Xavier 8 years ago
parent 96c1a046d4
commit b96385c4a7

@ -23,7 +23,6 @@ package org.isoron.uhabits;
import org.isoron.androidbase.*;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.models.sqlite.*;
import dagger.*;
@ -32,7 +31,6 @@ import dagger.*;
AppContextModule.class,
HabitsModule.class,
SingleThreadModule.class,
SQLModelFactory.class
})
public interface AndroidTestComponent extends HabitsApplicationComponent
{

@ -24,10 +24,12 @@ import android.test.suitebuilder.annotation.*;
import com.google.common.collect.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.core.models.sqlite.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.isoron.uhabits.database.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.rules.*;
@ -52,7 +54,7 @@ public class SQLiteHabitListTest extends BaseAndroidTest
private ModelFactory modelFactory;
private SQLiteRepository<HabitRecord> repository;
private Repository<HabitRecord> repository;
private ModelObservable.Listener listener;
@ -64,9 +66,8 @@ public class SQLiteHabitListTest extends BaseAndroidTest
fixtures.purgeHabits(habitList);
modelFactory = component.getModelFactory();
repository =
new SQLiteRepository<>(HabitRecord.class,
DatabaseUtils.openDatabase());
repository = new Repository<>(HabitRecord.class,
new AndroidSQLiteDatabase(DatabaseUtils.openDatabase()));
for (int i = 0; i < 10; i++)
{

@ -23,11 +23,12 @@ import android.support.annotation.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.database.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@ -50,7 +51,7 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest
private long day;
private SQLiteRepository<RepetitionRecord> sqlite;
private Repository<RepetitionRecord> sqlite;
@Override
public void setUp()
@ -61,8 +62,8 @@ public class SQLiteRepetitionListTest extends BaseAndroidTest
repetitions = habit.getRepetitions();
today = DateUtils.getStartOfToday();
day = DateUtils.millisecondsInOneDay;
sqlite = new SQLiteRepository<>(RepetitionRecord.class,
DatabaseUtils.openDatabase());
sqlite = new Repository<>(RepetitionRecord.class,
new AndroidSQLiteDatabase(DatabaseUtils.openDatabase()));
}
@Test

@ -19,13 +19,14 @@
package org.isoron.uhabits.models.sqlite;
import android.database.sqlite.*;
import android.database.sqlite.SQLiteDatabase;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.apache.commons.lang3.builder.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.database.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@ -37,7 +38,7 @@ import static org.junit.Assert.assertThat;
@MediumTest
public class SQLiteRepositoryTest extends BaseAndroidTest
{
private SQLiteRepository<ThingRecord> repository;
private Repository<ThingRecord> repository;
private SQLiteDatabase db;
@ -47,7 +48,8 @@ public class SQLiteRepositoryTest extends BaseAndroidTest
{
super.setUp();
this.db = DatabaseUtils.openDatabase();
repository = new SQLiteRepository<>(ThingRecord.class, db);
repository = new Repository<>(ThingRecord.class,
new AndroidSQLiteDatabase((db)));
db.execSQL("drop table if exists tests");
db.execSQL("create table tests(" +

@ -27,7 +27,7 @@ import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.reminders.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.core.ui.*;
import org.isoron.uhabits.models.sqlite.*;
import org.isoron.uhabits.database.*;
import org.isoron.uhabits.utils.*;
import org.isoron.uhabits.widgets.*;

@ -33,7 +33,6 @@ import org.isoron.uhabits.core.ui.screens.habits.list.*;
import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.io.*;
import org.isoron.uhabits.models.sqlite.*;
import org.isoron.uhabits.sync.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.widgets.*;
@ -42,7 +41,9 @@ import dagger.*;
@AppScope
@Component(modules = {
AppContextModule.class, HabitsModule.class, AndroidTaskRunner.class, SQLModelFactory.class
AppContextModule.class,
HabitsModule.class,
AndroidTaskRunner.class,
})
public interface HabitsApplicationComponent
{

@ -24,7 +24,7 @@ package org.isoron.uhabits;
import android.content.*;
import android.database.sqlite.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.database.*;
public class HabitsDatabaseOpener extends BaseSQLiteOpenHelper

@ -22,13 +22,16 @@ package org.isoron.uhabits;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.commands.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.reminders.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.core.ui.*;
import org.isoron.uhabits.database.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.notifications.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.utils.*;
import dagger.*;
@ -69,5 +72,19 @@ public class HabitsModule
{
return new WidgetPreferences(storage);
}
@Provides
public ModelFactory getModelFactory()
{
return new SQLModelFactory(
new AndroidSQLiteDatabase(DatabaseUtils.openDatabase()));
}
@Provides
@AppScope
public HabitList getHabitList(SQLiteHabitList list)
{
return list;
}
}

@ -0,0 +1,70 @@
/*
* Copyright (C) 2017 Á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.database;
import org.isoron.uhabits.core.db.*;
public class AndroidCursor implements Cursor
{
private android.database.Cursor cursor;
public AndroidCursor(android.database.Cursor cursor)
{
this.cursor = cursor;
}
@Override
public void close()
{
cursor.close();
}
@Override
public boolean moveToNext()
{
return cursor.moveToNext();
}
@Override
public Integer getInt(int index)
{
return cursor.getInt(index);
}
@Override
public Long getLong(int index)
{
return cursor.getLong(index);
}
@Override
public Double getDouble(int index)
{
return cursor.getDouble(index);
}
@Override
public String getString(int index)
{
return cursor.getString(index);
}
}

@ -0,0 +1,113 @@
/*
* Copyright (C) 2017 Á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.database;
import android.content.*;
import android.database.sqlite.*;
import org.isoron.uhabits.core.db.*;
import java.util.*;
public class AndroidSQLiteDatabase implements Database
{
private final SQLiteDatabase db;
public AndroidSQLiteDatabase(SQLiteDatabase db)
{
this.db = db;
}
@Override
public Cursor select(String query, String... params)
{
return new AndroidCursor(db.rawQuery(query, params));
}
@Override
public void execSQL(String query, Object... params)
{
db.execSQL(query, params);
}
@Override
public void beginTransaction()
{
db.beginTransaction();
}
@Override
public void setTransactionSuccessful()
{
db.setTransactionSuccessful();
}
@Override
public void endTransaction()
{
db.endTransaction();
}
@Override
public int update(String tableName,
Map<String, Object> map,
String where,
String... params)
{
ContentValues values = mapToContentValues(map);
return db.update(tableName, values, where, params);
}
@Override
public Long insert(String tableName, Map<String, Object> map)
{
ContentValues values = mapToContentValues(map);
return db.insert(tableName, null, values);
}
@Override
public void delete(String tableName, String where, String... params)
{
db.delete(tableName, where, params);
}
private ContentValues mapToContentValues(Map<String, Object> map)
{
ContentValues values = new ContentValues();
for (Map.Entry<String, Object> entry : map.entrySet())
{
if (entry.getValue() == null) continue;
if (entry.getValue() instanceof Integer)
values.put(entry.getKey(), (Integer) entry.getValue());
else if (entry.getValue() instanceof Long)
values.put(entry.getKey(), (Long) entry.getValue());
else if (entry.getValue() instanceof Double)
values.put(entry.getKey(), (Double) entry.getValue());
else if (entry.getValue() instanceof String)
values.put(entry.getKey(), (String) entry.getValue());
else throw new IllegalStateException(
"unsupported type: " + entry.getValue());
}
return values;
}
}

@ -19,7 +19,7 @@
*
*/
package org.isoron.androidbase.storage;
package org.isoron.uhabits.database;
import android.content.*;
import android.database.sqlite.*;

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,9 +15,11 @@
*
* 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;
package org.isoron.uhabits.database;
public class InconsistentDatabaseException extends RuntimeException
{

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,9 +15,11 @@
*
* 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;
package org.isoron.uhabits.database;
public class InvalidDatabaseVersionException extends RuntimeException
{

@ -1,20 +1,25 @@
/*
* Copyright (C) 2014 Markus Pfeiffer
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* 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/>.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.isoron.androidbase.storage;
package org.isoron.uhabits.database;
import java.io.BufferedInputStream;
import java.io.IOException;

@ -19,7 +19,7 @@
*
*/
package org.isoron.androidbase.storage;
package org.isoron.uhabits.database;
public class UnsupportedDatabaseVersionException extends RuntimeException
{

@ -21,7 +21,7 @@ package org.isoron.uhabits.sync;
import android.support.annotation.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.core.db.*;
@Table(name = "Events")
public class Event

@ -23,11 +23,12 @@ import android.support.annotation.*;
import android.util.*;
import org.isoron.androidbase.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.BuildConfig;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.commands.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.database.*;
import org.isoron.uhabits.utils.*;
import org.json.*;
@ -97,7 +98,7 @@ public class SyncManager implements CommandRunner.Listener
private SSLContextProvider sslProvider;
private final SQLiteRepository<Event> repository;
private final Repository<Event> repository;
@Inject
public SyncManager(@NonNull SSLContextProvider sslProvider,
@ -113,8 +114,8 @@ public class SyncManager implements CommandRunner.Listener
this.commandParser = commandParser;
this.isListening = false;
repository =
new SQLiteRepository<>(Event.class, DatabaseUtils.openDatabase());
repository = new Repository<>(Event.class,
new AndroidSQLiteDatabase(DatabaseUtils.openDatabase()));
pendingConfirmation = new LinkedList<>();
pendingEmit = new LinkedList<>(repository.findAll("order by timestamp"));

@ -26,7 +26,7 @@ import android.support.annotation.*;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.models.sqlite.*;
import org.isoron.uhabits.database.*;
import java.io.*;
import java.text.*;
@ -36,6 +36,7 @@ public abstract class DatabaseUtils
@Nullable
private static HabitsDatabaseOpener opener = null;
@Deprecated
public static void executeAsTransaction(Callback callback)
{
try (SQLiteDatabase db = openDatabase())

@ -9,6 +9,7 @@ dependencies {
apt 'com.google.auto.factory:auto-factory:1.0-beta3'
apt 'com.google.dagger:dagger:2.11-rc2'
compileOnly 'javax.annotation:jsr250-api:1.0'
compileOnly 'org.jetbrains:annotations-java5:15.0'
compileOnly 'com.google.auto.factory:auto-factory:1.0-beta3'
compileOnly 'com.google.dagger:dagger:2.11-rc2'
implementation 'com.android.support:support-annotations:25.3.1'

@ -19,7 +19,7 @@
*
*/
package org.isoron.androidbase.storage;
package org.isoron.uhabits.core.db;
import java.lang.annotation.*;

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,9 +15,24 @@
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/**
* Provides SQLite implementations of the core models.
*/
package org.isoron.uhabits.models.sqlite;
package org.isoron.uhabits.core.db;
public interface Cursor extends AutoCloseable
{
@Override
void close();
boolean moveToNext();
Integer getInt(int index);
Long getLong(int index);
Double getDouble(int index);
String getString(int index);
}

@ -0,0 +1,46 @@
/*
* Copyright (C) 2017 Á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.core.db;
import java.util.*;
public interface Database
{
Cursor select(String query, String... params);
int update(String tableName,
Map<String, Object> values,
String where,
String... params);
Long insert(String tableName, Map<String, Object> values);
void delete(String tableName, String where, String... params);
void execSQL(String query, Object... params);
void beginTransaction();
void setTransactionSuccessful();
void endTransaction();
}

@ -19,34 +19,35 @@
*
*/
package org.isoron.androidbase.storage;
package org.isoron.uhabits.core.db;
import android.content.*;
import android.database.*;
import android.database.sqlite.*;
import android.support.annotation.*;
import android.util.*;
import org.apache.commons.lang3.*;
import org.apache.commons.lang3.tuple.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
public class SQLiteRepository<T>
public class Repository<T>
{
@NonNull
private final Class klass;
@NonNull
private final SQLiteDatabase db;
private final Database db;
public SQLiteRepository(@NonNull Class<T> klass, @NonNull SQLiteDatabase db)
public Repository(@NonNull Class<T> klass, @NonNull Database db)
{
this.klass = klass;
this.db = db;
}
/**
* Returns the record that has the id provided.
* If no record is found, returns null.
*/
@Nullable
public T find(@NonNull Long id)
{
@ -54,30 +55,85 @@ public class SQLiteRepository<T>
id.toString());
}
/**
* Returns all records matching the given SQL query.
*
* The query should only contain the "where" part of the SQL query, and
* optinally the "order by" part. "Group by" is not allowed. If no matching
* records are found, returns an empty list.
*/
@NonNull
public List<T> findAll(String query, String... params)
public List<T> findAll(@NonNull String query, @NonNull String... params)
{
try (Cursor c = db.rawQuery(buildSelectQuery() + query, params))
try (Cursor c = db.select(buildSelectQuery() + query, params))
{
return cursorToMultipleRecords(c);
}
}
/**
* Returns the first record matching the given SQL query.
* See findAll for more details about the parameters.
*/
@Nullable
public T findFirst(String query, String... params)
{
try (Cursor c = db.rawQuery(buildSelectQuery() + query, params))
try (Cursor c = db.select(buildSelectQuery() + query, params))
{
if (!c.moveToNext()) return null;
return cursorToSingleRecord(c);
}
}
/**
* Executes the given SQL query on the repository.
*
* The query can be of any kind. For example, complex deletes and updates
* are allowed. The repository does not perform any checks to guarantee
* that the query is valid, however the underlying database might.
*/
public void execSQL(String query, Object... params)
{
db.execSQL(query, params);
}
/**
* Executes the given callback inside a database transaction.
*
* If the callback terminates without throwing any exceptions, the
* transaction is considered successful. If any exceptions are thrown,
* the transaction is aborted. Nesting transactions is not allowed.
*/
public void executeAsTransaction(Runnable callback)
{
db.beginTransaction();
try
{
callback.run();
db.setTransactionSuccessful();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
finally
{
db.endTransaction();
}
}
/**
* Saves the record on the database.
*
* If the id of the given record is null, it is assumed that the record has
* not been inserted in the repository yet. The record will be inserted, a
* new id will be automatically generated, and the id of the given record
* will be updated.
*
* If the given record has a non-null id, then an update will be performed
* instead. That is, the previous record will be overwritten by the one
* provided.
*/
public void save(T record)
{
try
@ -85,9 +141,9 @@ public class SQLiteRepository<T>
Field fields[] = getFields();
String columns[] = getColumnNames();
ContentValues values = new ContentValues();
Map<String, Object> values = new HashMap<>();
for (int i = 0; i < fields.length; i++)
fieldToContentValue(values, columns[i], fields[i], record);
values.put(columns[i], fields[i].get(record));
Long id = (Long) getIdField().get(record);
int affectedRows = 0;
@ -98,7 +154,7 @@ public class SQLiteRepository<T>
if (id == null || affectedRows == 0)
{
id = db.insertOrThrow(getTableName(), null, values);
id = db.insert(getTableName(), values);
getIdField().set(record, id);
}
@ -109,6 +165,10 @@ public class SQLiteRepository<T>
}
}
/**
* Removes the given record from the repository.
* The id of the given record is also set to null.
*/
public void remove(T record)
{
try
@ -170,30 +230,6 @@ public class SQLiteRepository<T>
field.getName());
}
private void fieldToContentValue(ContentValues values,
String columnName,
Field field,
T record)
{
try
{
if (field.getType().isAssignableFrom(Integer.class))
values.put(columnName, (Integer) field.get(record));
else if (field.getType().isAssignableFrom(Long.class))
values.put(columnName, (Long) field.get(record));
else if (field.getType().isAssignableFrom(Double.class))
values.put(columnName, (Double) field.get(record));
else if (field.getType().isAssignableFrom(String.class))
values.put(columnName, (String) field.get(record));
else throw new RuntimeException(
"Type not supported: " + field.getName());
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
private String buildSelectQuery()
{
return String.format("select %s from %s ",
@ -208,7 +244,7 @@ public class SQLiteRepository<T>
{
if (!(annotation instanceof Column)) continue;
Column column = (Column) annotation;
fields.add(new Pair<>(field, column));
fields.add(new ImmutablePair<>(field, column));
}
return fields;
}
@ -218,7 +254,7 @@ public class SQLiteRepository<T>
{
List<Field> fields = new ArrayList<>();
List<Pair<Field, Column>> columns = getFieldColumnPairs();
for (Pair<Field, Column> pair : columns) fields.add(pair.first);
for (Pair<Field, Column> pair : columns) fields.add(pair.getLeft());
return fields.toArray(new Field[]{});
}
@ -229,8 +265,8 @@ public class SQLiteRepository<T>
List<Pair<Field, Column>> columns = getFieldColumnPairs();
for (Pair<Field, Column> pair : columns)
{
String cname = pair.second.name();
if (cname.isEmpty()) cname = pair.first.getName();
String cname = pair.getRight().name();
if (cname.isEmpty()) cname = pair.getLeft().getName();
if (names.contains(cname))
throw new RuntimeException("duplicated column : " + cname);
names.add(cname);

@ -19,7 +19,7 @@
*
*/
package org.isoron.androidbase.storage;
package org.isoron.uhabits.core.db;
import java.lang.annotation.*;

@ -19,6 +19,9 @@
package org.isoron.uhabits.core.models;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
/**
* Interface implemented by factories that provide concrete implementations of
* the core model classes.
@ -44,4 +47,8 @@ public interface ModelFactory
ScoreList buildScoreList(Habit habit);
StreakList buildStreakList(Habit habit);
Repository<HabitRecord> buildHabitListRepository();
Repository<RepetitionRecord> buildRepetitionListRepository();
}

@ -19,28 +19,12 @@
package org.isoron.uhabits.core.models.memory;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import dagger.*;
@Module
public class MemoryModelFactory implements ModelFactory
{
@Provides
@AppScope
public static HabitList provideHabitList()
{
return new MemoryHabitList();
}
@Provides
@AppScope
public static ModelFactory provideModelFactory()
{
return new MemoryModelFactory();
}
@Override
public CheckmarkList buildCheckmarkList(Habit habit)
{
@ -70,4 +54,16 @@ public class MemoryModelFactory implements ModelFactory
{
return new MemoryStreakList(habit);
}
@Override
public Repository<HabitRecord> buildHabitListRepository()
{
throw new IllegalStateException();
}
@Override
public Repository<RepetitionRecord> buildRepetitionListRepository()
{
throw new IllegalStateException();
}
}

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,33 +15,30 @@
*
* 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;
package org.isoron.uhabits.core.models.sqlite;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import dagger.*;
import javax.inject.*;
/**
* Factory that provides models backed by an SQLite database.
*/
@Module
public class SQLModelFactory implements ModelFactory
{
@Provides
public static ModelFactory provideModelFactory()
{
return new SQLModelFactory();
}
private final Database db;
@Provides
@AppScope
public static HabitList provideHabitList(ModelFactory modelFactory)
@Inject
public SQLModelFactory(Database db)
{
return new SQLiteHabitList(modelFactory);
this.db = db;
}
@Override
@ -53,13 +50,13 @@ public class SQLModelFactory implements ModelFactory
@Override
public HabitList buildHabitList()
{
return SQLiteHabitList.getInstance(provideModelFactory());
return new SQLiteHabitList(this);
}
@Override
public RepetitionList buildRepetitionList(Habit habit)
{
return new SQLiteRepetitionList(habit);
return new SQLiteRepetitionList(habit, this);
}
@Override
@ -73,4 +70,16 @@ public class SQLModelFactory implements ModelFactory
{
return new MemoryStreakList(habit);
}
@Override
public Repository<HabitRecord> buildHabitListRepository()
{
return new Repository<>(HabitRecord.class, db);
}
@Override
public Repository<RepetitionRecord> buildRepetitionListRepository()
{
return new Repository<>(RepetitionRecord.class, db);
}
}

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,22 +15,22 @@
*
* 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;
package org.isoron.uhabits.core.models.sqlite;
import android.database.sqlite.*;
import android.support.annotation.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.utils.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import java.util.*;
import static org.isoron.uhabits.utils.DatabaseUtils.executeAsTransaction;
import javax.inject.*;
/**
* Implementation of a {@link HabitList} that is backed by SQLite.
@ -40,7 +40,7 @@ public class SQLiteHabitList extends HabitList
private static SQLiteHabitList instance;
@NonNull
private final SQLiteRepository<HabitRecord> repository;
private final Repository<HabitRecord> repository;
@NonNull
private final ModelFactory modelFactory;
@ -50,19 +50,18 @@ public class SQLiteHabitList extends HabitList
private boolean loaded = false;
@Inject
public SQLiteHabitList(@NonNull ModelFactory modelFactory)
{
super();
this.modelFactory = modelFactory;
this.list = new MemoryHabitList();
repository =
new SQLiteRepository<>(HabitRecord.class, DatabaseUtils.openDatabase());
this.repository = modelFactory.buildHabitListRepository();
}
private void loadRecords()
{
if(loaded) return;
if (loaded) return;
loaded = true;
list.removeAll();
@ -173,7 +172,7 @@ public class SQLiteHabitList extends HabitList
HabitRecord record = repository.find(habit.getId());
if (record == null) throw new RuntimeException("habit not in database");
executeAsTransaction(() ->
repository.executeAsTransaction(() ->
{
((SQLiteRepetitionList) habit.getRepetitions()).removeAll();
repository.remove(record);
@ -186,9 +185,8 @@ public class SQLiteHabitList extends HabitList
public synchronized void removeAll()
{
list.removeAll();
SQLiteDatabase db = DatabaseUtils.openDatabase();
db.execSQL("delete from habits");
db.execSQL("delete from repetitions");
repository.execSQL("delete from habits");
repository.execSQL("delete from repetitions");
getObservable().notifyListeners();
}
@ -208,18 +206,17 @@ public class SQLiteHabitList extends HabitList
Integer fromPos = fromRecord.position;
Integer toPos = toRecord.position;
SQLiteDatabase db = DatabaseUtils.openDatabase();
if (toPos < fromPos)
{
db.execSQL("update habits set position = position + 1 " +
repository.execSQL("update habits set position = position + 1 " +
"where position >= ? and position < ?",
new String[]{ toPos.toString(), fromPos.toString() });
toPos, fromPos);
}
else
{
db.execSQL("update habits set position = position - 1 " +
repository.execSQL("update habits set position = position - 1 " +
"where position > ? and position <= ?",
new String[]{ fromPos.toString(), toPos.toString() });
fromPos, toPos);
}
fromRecord.position = toPos;

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,18 +15,19 @@
*
* 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;
package org.isoron.uhabits.core.models.sqlite;
import android.support.annotation.*;
import android.support.annotation.Nullable;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.utils.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.jetbrains.annotations.*;
import java.util.*;
@ -36,17 +37,17 @@ import java.util.*;
*/
public class SQLiteRepetitionList extends RepetitionList
{
private final SQLiteRepository<RepetitionRecord> repository;
private final Repository<RepetitionRecord> repository;
private final MemoryRepetitionList list;
private boolean loaded = false;
public SQLiteRepetitionList(@NonNull Habit habit)
public SQLiteRepetitionList(@NonNull Habit habit,
@NonNull ModelFactory modelFactory)
{
super(habit);
repository = new SQLiteRepository<>(RepetitionRecord.class,
DatabaseUtils.openDatabase());
repository = modelFactory.buildRepetitionListRepository();
list = new MemoryRepetitionList(habit);
}

@ -15,9 +15,11 @@
*
* 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.core;
public class MyClass
{}
/**
* Provides SQLite implementations of the core models.
*/
package org.isoron.uhabits.core.models.sqlite;

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,12 +15,14 @@
*
* 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;
package org.isoron.uhabits.core.models.sqlite.records;
import org.apache.commons.lang3.builder.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
/**

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,11 +15,13 @@
*
* 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;
package org.isoron.uhabits.core.models.sqlite.records;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
/**

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,9 +15,11 @@
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/**
* Provides classes that represent rows in the SQLite database.
*/
package org.isoron.uhabits.models.sqlite.records;
package org.isoron.uhabits.core.models.sqlite.records;

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -15,40 +15,31 @@
*
* 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;
package org.isoron.uhabits.core.models.sqlite.records;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import org.isoron.androidbase.storage.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.models.sqlite.records.*;
import org.isoron.uhabits.utils.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.junit.*;
import org.junit.runner.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitRecordTest extends BaseAndroidTest
public class HabitRecordTest extends BaseUnitTest
{
private Habit habit;
private SQLiteRepository<HabitRecord> sqlite =
new SQLiteRepository<>(HabitRecord.class, DatabaseUtils.openDatabase());
@Before
@Override
public void setUp()
{
super.setUp();
habit = component.getModelFactory().buildHabit();
habit = modelFactory.buildHabit();
habit.setName("Hello world");
habit.setDescription("Did you greet the world today?");
habit.setColor(1);

@ -0,0 +1,42 @@
/*
* Copyright (C) 2017 Á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.core.models.sqlite.records;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.junit.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
public class RepetitionRecordTest extends BaseUnitTest
{
@Test
public void testRecord() throws Exception
{
Repetition rep = new Repetition(2000L, 50);
RepetitionRecord record = new RepetitionRecord();
record.copyFrom(rep);
assertThat(rep, equalTo(record.toRepetition()));
}
}
Loading…
Cancel
Save